name: tdd-coach description: “指导测试驱动开发,遵循红-绿-重构循环来编写聚焦行为的测试和最小化实现。当用户希望以TDD方式开发功能或修复Bug、提到"红绿重构"、需要进行集成测试、或要求测试先行开发时使用。”
测试驱动开发
核心理念
基本原则:测试应该通过公开接口验证行为,而不是验证实现细节。代码可以完全重写,但测试不应因此而变。
好的测试是集成风格的:它们通过公开 API 执行真实的代码路径。好的测试描述的是系统"做什么",而不是"怎么做"。一个好的测试读起来就像一份规格说明——"用户可以用有效购物车结账"清楚地说明了系统具备什么能力。这样的测试能在重构中存活下来,因为它们不关心内部结构。
坏的测试与实现耦合。它们 mock 内部协作者、测试私有方法、或通过外部手段(比如直接查询数据库而不是使用接口)来验证。一个危险信号是:你重构了代码,测试却挂了,但行为并没有变。如果你重命名了一个内部函数导致测试失败,说明那些测试测的是实现,不是行为。
参见 tests.md 了解示例,以及 mocking.md 了解 mock 使用指南。
反模式:水平切片
不要先写完所有测试,再写所有实现。 这就是"水平切片"——把红色阶段当成"写所有测试",绿色阶段当成"写所有代码"。
这样会写出糟糕的测试:
- 批量写的测试测的是_想象中的_行为,而不是_实际的_行为
- 你最终测的是_数据结构的形状_(数据结构、函数签名),而不是面向用户的行为
- 测试对真正的变化不敏感——行为坏了测试却通过,行为没问题测试却失败
- 你跑在了前灯之前,在理解实现之前就绑定了测试结构
正确做法:通过"示踪弹"做垂直切片。写一个测试 → 写一个实现 → 循环。每个测试都基于上一轮循环中学到的东西。因为你刚写完代码,你清楚地知道哪些行为重要、如何验证。
错误(水平切片):
红: test1, test2, test3, test4, test5
绿: impl1, impl2, impl3, impl4, impl5
正确(垂直切片):
红→绿:test1→impl1
红→绿:test2→impl2
红→绿:test3→impl3
...
工作流程
1. 规划
在写任何代码之前:
- [ ] 与用户确认需要哪些接口变更
- [ ] 与用户确认要测试哪些行为(确定优先级)
- [ ] 识别适合做深模块的机会(小接口,深实现)
- [ ] 为可测试性设计接口
- [ ] 列出需要测试的行为(不是实现步骤)
- [ ] 获得用户对计划的确认
要问:"公开接口应该是什么样的?哪些行为最重要、需要测试?"
你不可能测试所有东西。 与用户确认哪些行为最关键。把测试精力集中在关键路径和复杂逻辑上,而不是每个可能的边界情况。
2. 示踪弹
写一个测试,验证系统的一个行为:
红: 写第一个行为的测试 → 测试失败
绿: 写最少的代码让测试通过 → 测试通过
这就是你的示踪弹——证明这条路径端到端是通的。
3. 增量循环
对每个剩余的行为:
红: 写下一个测试 → 失败
绿: 写最少代码让它通过 → 通过
规则:
- 一次只写一个测试
- 只写刚好让当前测试通过的代码
- 不要预判未来的测试
- 保持测试聚焦在可观察的行为上
4. 重构
所有测试通过后,寻找重构候选:
- [ ] 提取重复代码
- [ ] 加深模块(把复杂性藏在简单接口后面)
- [ ] 在自然的地方应用 SOLID 原则
- [ ] 思考新代码揭示了已有代码的什么问题
- [ ] 每次重构后运行测试
绝不在红色阶段重构。 先到绿色阶段。
每轮循环检查清单
[ ] 测试描述的是行为,不是实现
[ ] 测试只使用公开接口
[ ] 测试能在内部重构中存活
[ ] 代码是针对当前测试的最小实现
[ ] 没有添加推测性的功能