测试驱动开发
TDD 就是红 → 绿的循环。本技能是一份参考,让这个循环产出的测试值得保留:什么样的测试是好测试、测试放在哪里、有哪些反模式,以及循环的规则。每一节在每一轮循环里都适用:要在循环之前和循环进行当中查阅,而不是等循环结束之后再查。
在探索代码库时,读一遍 GLOSSARY.md(如果存在),让测试名称和接口词汇与项目的领域语言对得上,并尊重你正在触及的那片区域里的 ADR。
什么样的测试是好测试
测试通过公开接口验证行为,不验证实现细节。代码可以整个换掉,测试不该跟着换。好测试读起来像一份规格说明:「用户可以用有效购物车结账」明确告诉你存在什么能力;它也能扛住重构,因为它不关心内部结构。
示例见 tests.md,模拟相关准则见 mocking.md。
接缝:测试放在哪里
接缝是你做测试的公开接口:你在这里观察行为,而不必伸手进去。测试只落在接缝上,绝不针对内部实现。
只在事先约定好的接缝上测试。 在写任何测试之前,先把准备测的接缝写下来,并跟用户确认。没有确认过的接缝上,一个测试都不写。你不可能什么都测,所以事先把接缝谈定,才能让测试精力落在关键路径和复杂逻辑上,而不是落在每个边缘情况上。
问一句:「公开接口是什么,我们应该测哪些接缝?」
当这个接口本身的形状还有疑问时(模块该有多深、接缝该放在哪里、接口该暴露什么),调用 Skill 工具并传入 "codebase-design" 来拿词汇。它是模块、接口、深度、接缝、适配器、杠杆和局部性这些术语的共同出处;它是一份随时查阅的参考,不是要跑起来的一次会话。
反模式
- 与实现耦合:模拟内部协作者、测试私有方法,或者通过旁路来验证(不去用接口,而是去查数据库)。识别信号:你重构了,但行为没有变,测试却挂了。
- 同义反复:断言用跟代码一样的方式把期望值重算一遍(
expect(add(a, b)).toBe(a + b),手工按同样方式推导出来的快照,把一个常量断言成它自己),于是它靠构造本身就成立,永远不可能与代码不一致。期望值必须来自独立的事实来源:一个已知正确的字面量、一个算好的例子,或者规格说明。 - 水平切片:先把所有测试写完,再写所有实现。批量写出来的测试验证的是_想象中_的行为:你测的是东西的_形状_,而不是面向用户的行为;测试对真实改动静默无感;而且你在还没理解实现之前就把测试结构定死了。改用垂直切片:一个测试 → 一份实现 → 重复,每个测试都是一发曳光弹,根据上一轮循环学到的东西来调整。
循环的规则
- 先红后绿。 先写失败的测试,然后只写刚好能让它通过的那点代码。不要提前考虑将来的测试,也不要加投机性的功能。
- 一次一个切片。 每一轮循环一条接缝、一个测试、一份最小实现。
- 重构不属于这个循环。 它属于评审阶段(见
code-review技能),不属于红 → 绿的实现循环。


