| name | tdd |
| description | 使用红-绿-重构循环的测试驱动开发. 当用户想要使用 TDD 构建功能或修复 bug, 提到 "测试驱动开发", "红绿重构", "red-green-refactor", 想要集成测试或请求测试优先开发时使用. |
测试驱动开发
哲学
核心原则: 测试应该通过公共接口验证行为, 而不是实现细节. 代码可以完全改变; 测试不应该改变.
好的测试是集成风格的: 它们通过公共 API 执行真实代码路径. 它们描述系统_做什么_, 而不是_如何做_. 一个好的测试读起来像一个规格说明 - "用户可以使用有效购物车结账"准确地告诉你存在什么能力. 这些测试在重构后仍然存活, 因为它们不关心内部结构.
坏的测试耦合到实现. 它们模拟内部协作者, 测试私有方法或通过外部方式验证(例如直接查询数据库而不是使用接口). 警告信号: 当你重构时测试中断, 但行为没有改变. 如果你重命名一个内部函数并且测试失败, 那些测试是在测试实现, 而不是行为.
参见 tests.md 获取示例和 mocking.md 获取模拟指南.
反模式: 水平切片
不要先写所有测试, 然后写所有实现. 这是 "水平切片" - 将 RED 视为 "写所有测试", 将 GREEN 视为 "写所有代码".
这会产生垃圾测试:
- 批量编写的测试测试_想象的_行为, 而不是_实际的_行为
- 你最终测试事物的_形状_(数据结构, 函数签名)而不是面向用户的行为
- 测试对真实变化不敏感 - 当行为中断时它们通过, 当行为良好时它们失败
- 你超越了你的前灯, 在理解实现之前就承诺了测试结构
正确方法: 通过追踪弹进行垂直切片. 一个测试 → 一个实现 → 重复. 每个测试都响应你从上一个循环中学到的东西. 因为你刚刚编写了代码, 你确切地知道什么行为重要以及如何验证它.
错误(水平):
RED: test1, test2, test3, test4, test5
GREEN: impl1, impl2, impl3, impl4, impl5
正确(垂直):
RED→GREEN: test1→impl1
RED→GREEN: test2→impl2
RED→GREEN: test3→impl3
...
工作流程
1. 规划
在编写任何代码之前:
询问: "公共接口应该是什么样子? 哪些行为最重要测试?"
你不能测试所有东西. 与用户确认哪些行为最重要. 将测试工作集中在关键路径和复杂逻辑上, 而不是每个可能的边缘情况.
2. 追踪弹
编写一个测试, 确认系统的一件事:
RED: 为第一个行为编写测试 → 测试失败
GREEN: 编写最小代码以通过 → 测试通过
这是你的追踪弹 - 证明路径端到端工作.
3. 增量循环
对于每个剩余行为:
RED: 编写下一个测试 → 失败
GREEN: 通过的最小代码 → 通过
规则:
- 一次一个测试
- 只需足够代码通过当前测试
- 不要预测未来的测试
- 保持测试专注于可观察的行为
4. 重构
所有测试通过后, 寻找重构候选:
永远不要在 RED 时重构. 先到达 GREEN.
每个循环的检查清单
[ ] 测试描述行为, 而不是实现
[ ] 测试仅使用公共接口
[ ] 测试将在内部重构后存活
[ ] 代码对于此测试是最小的
[ ] 没有添加投机性功能