mcpField
T
testing-craft

testing-craft

github.com/jssblck测试与质量01.2k 次安装3 周前更新
代码仓库

关于 testing-craft

testing-craft 是面向开发者的测试技能,按能力将行为交给最合适的证明或测试层,并聚焦于这些层无法证明的部分。

主要功能

  1. 01按层分配不变量的验证责任
  2. 02依据行为和故障模式选择测试
  3. 03使用类型化替身和真实协作者
  4. 04保持测试套件精简且确定
  5. 05检查回归敏感性并记录验证结果

适用场景

  1. 01规划代码变更的验证方案
  2. 02选择单元、集成或端到端测试
  3. 03审查测试能否发现实际缺陷

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jssblck/agents --skill testing-craft

安装后在 Claude Code 中输入/testing-craft确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
languages/
go.md
python.md
rust.md
typescript.md
references/
episodes.md

详细介绍

测试工艺

测试只负责证明没有更强层级能够证明的内容。每条不变量都由一个层级负责:code-craft 证明阶梯中实际能够证明它的最高层级。顺序为:类型和 lint 工具、效应、证明、数据库约束、单元测试、集成测试、端到端测试。尽可能让错误情况无法通过编译或构造,然后在能够证明剩余内容的最高测试层级进行测试,并尽量减少测试数量,只保留足以支撑该证明的测试。

范围与验证

  • 明确要更改的行为,以及每个新增或修改的测试应捕获的具体缺陷。测试不必检测无关缺陷。
  • 测试新代码之前,列出其失败模式,并将每种模式交给能够负责它的最高层级。
  • 优先遵循项目说明和现有测试惯例,而非这些默认规则。
  • 运行受影响的测试和必要检查。如果更改的依赖、故障或未解决的风险有此必要,则扩大覆盖范围。
  • 不要仅为了满足测试偏好而更改生产架构。为实现所请求的修复,需要时可以重构;独立的重构则应另行提出。
  • 如果现有自动化已覆盖更改的行为,就使用它。手动测试在能捕获自动化无法发现的风险时很有用,例如视觉布局。
  • 需要运行器或 fixture 指引时,只阅读下方相关语言的文件。设计指引位于 code-craft 和 database-craft 中。

测试剩余内容

类型无法证明计算、解析边界情况、SQL、排序、时序、远程行为、渲染或进程间连线。对这些内容,使用能够证明它们的最高测试层级:

属性层级方法
纯计算:格式化、排序、差异比较、日期计算、解析器单元使用实际值直接调用
服务逻辑单元在声明的依赖项处使用带类型的替身
渲染出的控件单元使用真实组件、带类型的 props 和注入的服务
查询、存储库、约束、作业、迁移集成使用真实的 Postgres(database-craft)
提供商 SDK 和传输集成使用真实客户端对接 Vercel Emulate
原生模块、文件、进程集成使用真实代码、fixture SQLite、临时目录和一次性 Git 存储库
导航、身份验证、进程与平台间连线端到端使用少量确定性用例作为发布门禁

单元测试优于集成测试,集成测试优于端到端测试:每降低一个层级,测试就更慢、更容易产生噪声,且每次运行覆盖的输入更少。只有在更高层级无法验证时,才选择较低层级。

精简测试套件

  • 不要测试类型、lint 规则、效应签名、证明或约束已经证明的内容。
  • 每项属性只在一个层级证明。路由测试不重复其存储库的 SQL 用例;端到端流程不重复格式化函数的边界情况。契约测试套件是唯一有意设置的例外。
  • 当更高层级接管某项测试的用例时,删除该测试并注明新的负责人。删除前先运行任何替代测试。
  • 移动或删除覆盖范围时,保留仍需测试的所有权、并发、取消、无效输入和失败情况。
  • 使用编译失败检查守护证明和所有权边界:这些检查通过断言类型错误,确保放宽证明类型会导致构建失败。不要为普通类型添加此类检查。
  • 不要在测试中复述实现,也不要仅为测试而公开私有辅助函数。

双打

只有在更强的测试层级发挥作用后,双打才合适。类型固定依赖项的形状,效果固定它的要求和失败方式,证明固定它的前置条件,因此在单元测试中,类型化双打可以替代其行为。但双打无法证明真实依赖项仍按这种方式运行;这需要由冒烟测试覆盖。

  • 基于真实接口进行类型检查。 编译器必须能拒绝形状不符的双打:根据服务接口构建双打,或将其声明为真实函数的类型。未类型化的双打(as any、as unknown as)属于类型断言。
  • 替换依赖项,而非被证明的对象。 测试 SQL 需要 Postgres,测试提供商的协议需要模拟器。调用它们的逻辑可以使用双打。绝不要双打被测单元。
  • 优先使用现有接缝。 在尝试模块模拟(它会让测试依赖文件布局)之前,先通过服务标签提供测试实现,或传入类型化参数。
  • 驱动已声明的失败。 类型化失败便于通过双打触发每种错误变体;覆盖调用方会处理的错误。
  • 断言结果。 只有当调用本身就是契约时才断言调用,例如发送一封邮件、缓存命中时跳过网络请求,或在写入前检查授权。只断言相关调用及参数。
  • 当伪实现承载行为时使用契约测试套件。 当共享伪实现模拟了许多测试所依赖的状态、分页或错误时,编写一套每种实现都必须满足的测试用例,并同时针对伪实现和真实实现运行。真实行为改变时,先修改契约。
冒烟测试是哨兵

每个集成点都至少保留一个针对真实对象的测试:Postgres、每个提供商(通过 Emulate 或提供商的沙盒)、每个进程边界,以及通过端到端测试覆盖的每个应用界面。覆盖主路径,以及只有真实对象才可能引发问题的部分:身份验证、序列化、传输。冒烟测试用于在集成点发生变化时失败;它不是单元测试用例的另一份副本。

真实协作者

在集成测试层级,使用真实存储:隔离的数据库实例、fixture SQLite、临时目录和一次性 Git 仓库。

对于提供商和服务 API,请使用 Vercel Emulate。优先使用内置提供商;对于不受支持的 API 或项目自身的服务,则使用自定义模拟器。通过项目的技能安装器将其上游 emulate 技能安装到仓库中,并在使用前阅读。在每个导入它的工作区中声明测试依赖项。

  • 通过原生 HTTP 驱动真实 SDK 或客户端。通过现有依赖边界注入模拟器的 URL。保留标头、请求体和取消行为。
  • 在自定义模拟器路由中,使用应用程序采用的同一套架构解码请求体,并保留可观察状态。在 HTTP 服务器后提供预设成功响应并不能证明集成有效。
  • 为每个 fixture 提供独立状态和由操作系统分配的端口(port: 0)。在场景之间重置或重新创建状态,并在失败后也等待清理完成。绝不要在并行工作树之间共享可变状态、固定端口或持久化文件。
  • 通过服务边界测试相关失败。当授权、分页、重试和取消属于契约的一部分时,检查这些行为。

如果工具存在限制,应如实报告,而不是悄悄改用保真度更低的测试,并将其称为集成测试或端到端测试。

端到端测试是发布门槛

保留一组规模精简、结果确定的测试,驱动正在运行的应用、公共 CLI 或打包后的应用,并断言用户可观察到的结果,包括持久化效果。发布门槛只依据确定性的断言;模型评估可以解释失败,但绝不能作为门槛。仅测试渲染组件,无法证明导航、身份验证或原生平台连接正常。

避免镜像实现的测试

源码断言和精确的内部调用脚本往往会锁定实现,却无法保护其契约。对于可疑的测试,请明确:

  • 它保护的行为。
  • 会导致它失败的具体缺陷。
  • 它是否会拒绝行为相同的有效实现。

将脆弱的断言改写为检查结果、状态、渲染输出或外部效果。如果某个测试无法发现更强一层测试未能发现的真实缺陷,就删除它。保留用于落实文档所述仓库规范的检查。

编写易读的测试

  • 明确说明场景和预期结果。让因果关系一目了然。
  • 在测试主体中保留相关输入和预期结果。辅助函数可以隐藏无关的构造过程;针对并行场景时,可以使用表驱动用例。
  • 对一组用例使用同一个 check(input, expected) 辅助函数,这样 API 变更时只需修改辅助函数,而不用改每个测试。预期输出较大或经常变化时,使用 inline snapshot(expect)测试。
  • 不要通过重复生产算法来计算预期结果。
  • 选择能暴露输入交换或意外默认值的不同值。在行为测试涉及空值、零值和边界值时,应将它们纳入测试。
  • 断言重要字段。若整个对象就是契约,应使用完整对象相等性断言,而不是对偶然细节做宽泛的快照断言。
  • 让失败信息显示预期值和实际值。
  • 覆盖相关的失败路径和边界。只有在不变量或庞大的输入空间确有需要时才使用属性测试,不要仅仅因为有现成的库就使用。

保持测试确定性

  • 等待完成信号或带超时的可观察条件;不要随意休眠一段时间并假定任务已完成。
  • 如果时间和随机性会影响结果,就对其加以控制。
  • 在测试之间隔离可变状态。
  • 通过受控依赖项或输入,使重要失败可复现。

确认回归检测能力

修复 Bug 时,先在能展示问题的最强一层复现失败:能拒绝错误状态的类型,或失败的测试。确认失败原因正是预期原因,然后确认修复后测试通过。如果无法复现真实失败,请说明已有证据和仍存的不确定性。

对于其他变更,请明确断言能检测到的缺陷。如果检测能力不确定,或风险值得验证,请使用定向变异测试。如果修改生产代码,请隔离实验,并在继续之前还原代码。

反转断言只能检查代码是否执行,无法检查其对目标缺陷的检测能力。常规测试修改和从通过到通过的重构不需要修改生产代码。保留已覆盖的行为,并比较重构前后的相关结果。说明无法执行的验证。

留下可重复验证的证据

明确保证内容:哪些无效输入、状态或调用现在无法编译或构造,以及具体位置。对于每个保留的测试,说明它能捕获的缺陷及其所属层级。

每个端到端测试都应以可验证的产物收尾:准确的命令或可重放流程,以及生成的输出、截图或录制内容。使用项目规定的产物目录和发布规则。

迭代期间运行针对性场景,并在交付前通过所需的全部检查。只有在相关修改、失败或尚未解决的风险之后,才重复已通过的检查。更改慢速测试套件的执行方式前,先测量其耗时;优化运行时间时,保留隔离性和有效覆盖。

语言参考

需要了解运行器、固定装置或异步测试的细节时,请阅读相关文件:

其他语言请遵循项目约定。

来源

改编自 Google 的 Testing on the Toilet 系列,涵盖以行为为重点的测试、测试替身、DAMP 和 SMURF。源文章索引见 references/episodes.md,来源包括 shamashel/testing-on-the-toilet,以及 matklad 的 How to Test。

评论

支持 Markdown