Test-Driven Development (TDD)
概述
先写测试。亲眼看着它失败。只实现刚好能让它通过的最少代码。在保持测试全绿的同时重构。如果你没看到测试失败,你就不知道它测试的是不是正确的行为。
铁律
没有失败的测试就不写生产代码。
先写了代码再补测试?删掉它。从测试重新开始。
何时使用
每个功能、缺陷修复、重构或行为变更。
例外只有在获得人工明确批准时才允许(一次性原型、生成代码等)。
红-绿-重构循环
RED(红)——编写一个描述期望行为的最小测试。
名称清晰、具体。
测试真实行为(避免不必要的 mock)。
验证 RED ——运行测试,确认它因预期原因失败。
GREEN(绿)——编写最简单、能让测试通过的代码。不要额外功能。
验证 GREEN ——运行测试套件,确保新测试与既有测试全部通过。
REFACTOR(重构)——在保持全部测试绿色的前提下清理代码和测试。
为下一个行为重复上述过程。
好测试与坏测试
好:测试单一事物;名称清晰;测试真实行为。 坏:含糊的「能工作」;命名为 test1;对 mock 进行断言。
为什么顺序很重要
先写代码后写测试会立即通过 → 什么也证明不了。
手动测试不是可复现的证据。
把预先写好的代码「留作参考」会破坏 TDD——删除它,从测试重建。
合理化借口的应对
借口:「太简单了」→ 回应:简单代码也会坏——照样测试。 借口:「我以后再补测试」→ 回应:后补的测试确认的是既有行为,而非需求。 借口:「手动测试就够了」→ 回应:没有记录、无法重跑;必须自动化。 借口:「删除工作太浪费」→ 回应:这是沉没成本;未测试的代码就是债务。 借口:「要务实」→ 回应:TDD 能避免调试地狱;务实即可靠。
验证清单
每个新行为都由一个测试守护。 在实现之前看到每个测试失败。 每次失败都源于缺失的行为(而不是拼写错误)。 编写最少代码让每个测试通过。 变更后完整测试套件为绿色。 只在绿色状态下进行重构。 测试覆盖边界与错误情况。 有任何一项未满足?你跳过了 TDD——重新开始。
集成缺陷修复
先用一个失败的测试复现缺陷。 应用修复以满足该测试。 保留该测试以防回归。
最终规则
生产代码只应与一个在写代码之前就已失败的测试同时存在。否则,用 TDD 重做。