mcpField
W
writing-unit-tests

writing-unit-tests

github.com/riekelt测试与质量618.9k 次安装7 周前更新
代码仓库

关于 writing-unit-tests

面向编写或修改单元测试的工程师,强调验证行为契约而非实现细节。内容涵盖确定性测试、清晰命名,以及对失败路径的有效断言。

主要功能

  1. 01测试公开行为而非实现
  2. 02每个测试聚焦一个具名行为
  3. 03编写无休眠的确定性测试
  4. 04有选择地模拟外部边界
  5. 05断言结果与失败路径

适用场景

  1. 01为修改后的单元编写测试
  2. 02替换耦合内部实现的测试
  3. 03排查异步测试的不稳定问题
  4. 04增加错误行为覆盖

安装

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

$npx skills add https://github.com/riekelt/principal-engineer --skill writing-unit-tests

安装后在 Claude Code 中输入/writing-unit-tests确认已加载。

详细介绍

编写单元测试

必备背景: principal-engineering 技能。testing-changes 规定一次变更需要哪些测试;本技能讲的是如何编写测试。

概述

单元测试是一项带有名称的行为断言,下一位工程师会在构建失败时阅读它。核心原则:测试契约,而非实现。 名称承载断言,测试应足够简单,以免测试本身出错。

契约优先于实现

  • 通过单元的公开契约进行测试。保留行为的重构不应导致测试失败;若确实失败,说明测试断言的是实现。
  • 不要断言调用顺序、内部状态,或方法 A 调用了方法 B,除非该交互本身就是契约(边界上必须发生的副作用)。
  • 绝不要从生产代码的算术逻辑推导预期值,既不要重新实现公式,也不要调用计算该值的共享辅助函数。预期值应是独立推算出的字面量(手动计算、根据规范或真实数据得出),并在注释中说明推导过程。

每个测试只测一种行为,并以断言命名

  • 每个测试只测一种行为;拆分测试比事后分析一个有多项断言的失败更省力。
  • 名称应说明对象、场景和预期结果:expired_token_is_rejected_with_401,而非 test_auth_3。测试名称应描述行为、状态转换和不变量;绝不要描述交付顺序、工单编号或阶段。
  • 按清晰可见的顺序编排、执行、断言。测试中不要包含分支、循环或逻辑:包含逻辑的测试本身就需要测试。共享设置可使用构建器或按角色命名的测试夹具;用途不明的测试夹具会掩盖断言所依赖的安排事实。生成和迭代应放在构建器和辅助函数中,而非测试主体中。基于属性的测试是测试不变量的合适形式,并应遵循其框架的结构;基于示例的测试应保持无逻辑。

确定性

  • 单元测试中不要使用真实时间、真实随机性、真实网络或真实文件系统:注入时钟、固定随机种子或注入随机数,并模拟边界。
  • 不要使用休眠。等待异步工作时,应基于条件(在截止时间前轮询可观察到的结果),而不是等待固定时长。
  • 不稳定的测试就是失败的测试:修复它,或明确隔离并注明负责人(参见 testing-changes 中的失败测试规则);反复运行直到通过,就是在屏蔽检测器。

模拟即假设

  • 模拟不由你掌控的边界(网络、时钟、文件系统、第三方服务);对于单元范围内由你掌控的代码,优先使用真实协作者。对于包裹非自有资源的自有封装(例如连接数据库的仓库类),应在自有代码最后接触非自有资源的接缝处进行模拟,并始终使用按角色命名且清晰可见的测试数据。每个模拟都硬编码了对契约的一项假设;模拟过时,会让测试套件在真实集成已经损坏时仍然全绿。
  • 如果测试大部分内容都是模拟连接,它测的就是模拟。要么扩大单元范围,纳入真实行为;要么接受这个接缝需要集成测试(并说明是哪种测试)。
  • 测试夹具是对现实的标注快照:应尽可能精简,按其在场景中的作用命名,契约变更时有意更新,绝不要盲目重新生成来让失败的测试转绿。

断言与失败路径

  • 用值断言结果,而非断言没有抛出异常。“没有抛出异常”几乎不能说明任何问题。
  • 失败路径也是一等测试对象:应暴露带类型的失败、大声进入降级模式,并确保保护措施确实有效(参见 handling-failures)。
  • 测试本身也应遵循不静默吞错契约:测试代码中不得捕获并忽略错误,也不得使用在前置条件缺失时静默跳过的条件断言。无法运行的测试必须失败,或明确跳过并说明原因。

常见错误

  • 镜像测试:重新实现生产逻辑来计算预期结果。
  • 模拟回音室:模拟自己的类,然后断言该模拟。
  • 超大型测试:包含十二条断言,却只有一个名称,无法得知哪条断言失效。
  • 共享可变测试夹具导致测试顺序影响结果;每个测试都应构建或接收自己的状态。
  • 用 sleep “修复”不稳定测试,只是让问题不那么常见。
  • 让测试夹具通过检查:修改预期值以匹配实际输出,却不推导新值为何正确。

评论

支持 Markdown