| name | test-driven-development |
| description | 实现功能、修复 bug 或重构行为时使用——先验证失败测试,再实现当前 Task 的最终行为并保持其测试通过 |
测试驱动开发
先写测试,确认它因目标行为缺失而失败,再编写最小实现使当前 Task 的目标测试通过。
核心原则: 没有观察到正确的失败,就不能证明测试验证了目标行为。
失败测试 → 验证失败原因 → 最小实现 → 目标测试通过 → 整理 → 再次通过
适用范围
| 场景 | 行为 |
|---|
| 新功能 | 必须使用 TDD |
| bug 修复 | 先写可重现问题的失败测试 |
| 行为重构 | 先用测试锁定行为 |
| 纯文档和静态资源 | 不要求构造虚假测试 |
| 生成文件或机械配置 | 使用与产物相匹配的验证 |
红—绿—重构
digraph tdd {
rankdir=LR;
"编写最小失败测试" [shape=box];
"失败原因正确?" [shape=diamond];
"修正测试" [shape=box];
"编写最小实现" [shape=box];
"目标测试通过?" [shape=diamond];
"修正实现" [shape=box];
"整理代码" [shape=box];
"再次运行目标测试" [shape=box];
"编写最小失败测试" -> "失败原因正确?";
"失败原因正确?" -> "修正测试" [label="否"];
"修正测试" -> "失败原因正确?";
"失败原因正确?" -> "编写最小实现" [label="是"];
"编写最小实现" -> "目标测试通过?";
"目标测试通过?" -> "修正实现" [label="否"];
"修正实现" -> "目标测试通过?";
"目标测试通过?" -> "整理代码" [label="是"];
"整理代码" -> "再次运行目标测试";
}
红:编写失败测试
测试必须:
- 只验证一个明确行为
- 名称描述业务结果
- 使用真实代码,除非外部系统使 mock 无法避免
- 包含清晰输入、输出和错误断言
- 因目标行为缺失而失败
测试通过说明它覆盖的是已有行为,需要修正测试。测试因语法、导入或环境错误中止,不是有效红阶段。
绿:实现目标行为
编写使当前测试通过的最小最终实现:
- 不添加测试未要求的能力
- 不为开发期间保持服务运行而添加兼容层
- 不保留随后会删除的临时接口
- 遵循设计定义的最终命名和结构
重构:保持目标测试通过
只在目标测试通过后:
- 移除重复
- 改进命名
- 提取必要辅助函数
- 删除失效代码
整理后再次运行相同测试。
smart-exec-plan 中的验证边界
smart-exec-plan 连续执行全部 Stage。Task 和 Stage 不是部署边界,因此当前 Task 的绿色状态只要求其计划规定的目标测试通过。
中间 Task 可以存在:
- 尚未迁移的调用方
- 暂时失败的完整构建
- 暂时无法启动的服务
- 将由后续已定义 Task 完成的集成
这不允许当前 Task 忽略自身失败。以下情况仍须立即修正:
- 当前 Task 新增或修改的测试失败
- 失败与后续 Task 无关
- 出现计划外错误
- 数据可能丢失或损坏
- 测试通过依赖虚假断言或过度 mock
全部 Stage 完成后必须运行完整验证,届时不得保留任何中间失败。
bug 修复
重现 bug 的失败测试
→ 确认测试因该 bug 失败
→ 最小修复
→ 回归测试通过
→ 相关测试通过
不得先修改生产代码再补回归测试。
测试质量
好的测试应具备:
- 最小:一个测试只表达一个行为
- 清晰:名称说明预期结果
- 敏感:业务规则改变时相关测试会失败
- 真实:验证真实代码交互,而不是只验证 mock
- 稳定:不依赖任意等待时间和共享状态
测试是行为规格,不是覆盖率装饰。无法写出准确测试通常说明接口或职责仍不清楚,应停止并检查设计。
禁止做法
- 先写实现后补测试
- 保留预先写好的实现作为参考再声称采用 TDD
- 测试一开始就通过却继续实现
- 把导入错误当作正确红阶段
- 为了通过测试修改正确断言
- 只测试 mock 调用次数而不测试行为
- 用宽泛快照代替关键业务断言
- 为中间服务可用性增加最终不需要的兼容代码
- 在最终验证失败时以“中间状态”为理由继续
Task 完成检查
- 已看到目标测试正确失败
- 失败原因是目标行为缺失
- 已实现设计规定的最终行为
- 当前 Task 的目标测试通过
- 整理后测试仍通过
- 没有计划外改动
- 没有纯开发过渡兼容代码