测试驱动开发(TDD)
先用测试定义行为,再用最小实现满足它,并在测试保护下持续重构的开发方法。
#type / concept
#status / evergreen
#tech / dev / test
[!info] related notes
- 所属 MOC: 测试驱动开发 MOC, Testing MOC
- 相关概念: 自测试代码(Self-Testing Code)
- 并列概念: TDD Workflow
- 易混淆概念: 前端测试, 后端测试
- 关系笔记: AI 编码时代的 TDD, TDD 中测什么与测试金字塔
测试驱动开发(TDD)
一句话定义
TDD 不是“多写测试”,而是先写一个失败的测试来定义下一个行为,再写最小实现让它通过,最后在测试保护下重构设计。
它到底驱动什么
TDD 驱动的重点不是测试数量,而是设计过程本身。
- 先写测试,会先逼你想清楚接口、调用方式和外部可观察行为
- 最小实现会限制你不要顺手把未来需求提前写进去
- 重构步骤会把“先跑通”升级成“结构也干净”
所以 TDD 更接近一种设计和实现的微循环,而不是 QA 工作前移。
它不是什么
- 不是“写完代码后顺手补测试”
- 不是“为了覆盖率把 trivial code 也测一遍”
- 不是“只做 Red-Green,不做 Refactor”
- 不是“把所有测试都叫 TDD”
如果代码已经有自动化测试保护,那叫 自测试代码(Self-Testing Code);TDD 是达到这类代码的一种强方法,但不是唯一方法。
TDD 真正解决的问题
1. 快速反馈
你不需要等到联调、提测或线上故障才知道偏离了需求,小测试会在几十毫秒到几秒内给出信号。
2. 改善设计
因为思考顺序从“怎么用它”开始,而不是“我想怎么实现”,接口通常会更稳定,依赖也更容易被隔离。
3. 支持安全重构
TDD 的第三步不是装饰,而是核心。只有在测试持续变绿的前提下重构,代码才能同时保持可用和可维护。
4. 回归保护
成熟团队遇到 bug 时,第一反应通常不是直接修,而是先写一个能稳定复现 bug 的测试,再修实现,让 bug 不会轻易回来。
它最适合驱动什么
- 业务规则、计费、权限、状态流转
- 解析、转换、校验逻辑
- 纯函数、领域服务、核心模块
- 历史 bug 的回归保护
- 对外部系统的契约边界
它不适合一上来就重度使用的场景
- 强视觉、强交互、还在快速试样的 UI 探索阶段
- 一次性脚本和临时 spike
- 需求与接口还明显摇摆的原型阶段
但即使这些场景不严格按 TDD 开始,一旦需求稳定,关键路径仍然应该补成 自测试代码。
在 AI 时代为什么更重要
AI 很会写“像对的代码”,但不一定真的对。
这让 TDD 在 AI 时代的角色从开发习惯升级成:
- 可执行规格
- 局部任务边界
- 自动验收器
- 重构安全网
也就是说,TDD 不只是帮助人类收敛设计,也是在帮助 agent 不要乱写、跳步和幻觉式完成任务。
详见 AI 编码时代的 TDD。
最短记忆方式
TDD = 先用测试定义行为,再用最小实现满足它,最后在测试保护下重构。