| name | tdd |
| description | 测试驱动开发。当用户想要以测试先行的方式构建功能或修复 bug、提到"red-green-refactor",或想要集成测试时使用。 |
Test-Driven Development
TDD 就是红 → 绿的循环。这个技能是让那个循环产出值得保留的测试的参考:什么是好测试、测试放在哪里、反模式,以及循环的规则。每个小节在每个循环上都适用——在循环之前和之中参考它们,而不是之后。
探索代码库时,阅读 CONTEXT.md(如果存在),让测试名称和接口词汇与项目的领域语言相匹配,并尊重你所触及区域内的 ADR。
什么是好测试
测试通过公共接口验证行为,而不是实现细节。代码可以彻底改变;测试不应改变。一个好测试读起来像一份规格说明——"用户可以用有效的购物车结账"精确地告诉你存在什么能力——并且能在重构中存活,因为它不在乎内部结构。
示例见 tests.md,mock 指南见 mocking.md。
接缝——测试放在哪里
**接缝(seam)**是你在其上测试的公共边界:你在其处观察行为而不伸手到内部的接口。测试位于接缝处,绝不针对内部实现。
只在事先约定的接缝处测试。 在写任何测试之前,写下被测的接缝并与用户确认。不在未确认的接缝处写任何测试。你无法测试一切——预先约定接缝,是让测试精力落在关键路径和复杂逻辑上、而不是每一个边界情况上的方式。
问:"公共接口是什么,我们应该在哪些接缝处测试?"
反模式
- 与实现耦合——mock 内部协作者、测试私有方法,或通过一条旁路验证(查询数据库而不是使用接口)。征兆:当你重构时测试就崩,但行为并没有改变。
- 同义反复(Tautological)——断言以代码相同的方式重新计算期望值(
expect(add(a, b)).toBe(a + b),一份用手以同样方式导出的快照,一个断言等于自身的常量),所以它按构造就通过,永远不可能与代码发生分歧。期望值必须来自一个独立的真理来源——一个已知良好的字面量、一个演算过的例子、规格说明。
- 横向切片——先写所有测试,再写所有实现。批量测试验证的是想象中的行为:你测试的是事物的形状而非面向用户的行为,测试变得对真实改动不敏感,而且你在理解实现之前就把自己绑定到了测试结构上。改为以垂直切片工作——一个测试 → 一个实现 → 重复,每个测试都是一颗曳光弹,对上一个循环所教给你的东西作出回应。
循环的规则
- 先红后绿。 先写会失败的测试,然后只写刚好让它通过的代码。不要预判未来的测试,也不要添加投机性的功能。
- 一次一个切片。 每个循环一个接缝、一个测试、一个最小实现。
- 重构不是循环的一部分。 它属于评审阶段(见
code-review 技能),而不属于红 → 绿的实现循环。