测试驱动开发
TDD 是红→绿循环。此技能是使该循环产生值得保留的测试的参考:什么是好的测试、测试放在哪里、反模式以及循环的规则。每个部分的规则在每个循环中都要应用:在循环之前和期间(而不是之后)查阅它们。
在探索代码库时,请阅读GLOSSARY.md(如果存在),以便测试名称和接口词汇与项目的领域语言相匹配,并尊重您所触及区域内的 ADR。
什么是好的测试
测试通过公共接口验证行为,而不是实现细节。代码可以完全改变;测试不应改变。一个好的测试读起来像一份规范:“用户可以使用有效购物车结账”准确告诉你存在什么功能,并且它在重构后依然有效,因为它不关心内部结构。
参见 tests.md 获取示例,参见 mocking.md 获取模拟指南。
接缝:测试的位置
接缝是您进行测试的公共边界:无需窥探内部即可观察行为的接口。测试位于接缝处,绝不针对内部逻辑。
仅应在预先商定的接缝处进行测试。 在编写任何测试之前,写下正在测试的接缝并与用户确认。没有测试是在未确认的接缝上编写的。您无法测试所有内容,因此提前商定接缝是将测试工作投入到关键路径和复杂逻辑(而不是每个边缘情况)的方法。
询问:"公共接口是什么,我们应该测试哪些接缝?" 为每个提议的接缝添加一行注释,说明它捕获了什么以及遗漏了什么。
当接口本身的形状存在疑问时(模块的深度、接缝的位置、接口应该暴露什么),请调用 Skill 工具,参数为 "codebase-design" 以获取相应词汇表。它是关于模块、接口、深度、接缝、适配器、杠杆和局部性等术语的共享来源,是一个参考(而不是一个要运行的会话)。
反模式
- 实现耦合:模拟内部协作者、测试私有方法,或通过侧向通道验证(查询数据库而非使用接口)。标志:当你重构但行为未改变时测试失败。
- 同义反复:断言重新计算了与代码相同的方式的期望值(
expect(add(a, b)).toBe(a + b),手动以相同方式推导的快照,断言一个常量等于自身),因此它通过构造而通过,永远不能与代码产生分歧。期望值必须来自独立的真相来源:已知正确的字面量、一个手工计算的例子、规范。 - 水平切片:先编写所有测试,再编写所有实现。批量测试验证的是想象中的行为:你测试的是事物的形状而不是面向用户的行为,测试对真实变化变得不敏感,并且你在理解实现之前就承诺了测试结构。应该采用垂直切片方式:一个测试 → 一个实现 → 重复,每个测试都是一颗示踪弹,响应上一个循环教会你的事情。
循环的规则
- 红在前,绿在后。 先编写失败的测试,然后只编写足够通过它的代码。不要预期未来的测试或添加推测性功能。
- 一次一个切片。 每个循环一个接缝、一个测试、一个最小的实现。
- 重构不是循环的一部分。 重构属于评审阶段(参见
code-review技能),而不是红→绿实现循环的一部分。