| name | test-driven-development |
| description | 测试驱动开发方法论 —— 红-绿-重构循环,先写测试再写实现 |
| category | testing |
| loading | on-demand |
| triggers | {"keywords":["写测试","TDD","测试驱动","先写测试","tdd","测试用例"]} |
这是什么
测试驱动开发是一种在编写实现代码之前先编写测试的开发方法论。通过「红—绿—重构」的短周期循环,确保每一行生产代码都有对应的测试保护。
何时使用
- 新功能开发,需求明确且可验证
- 修复 Bug 时,先写复现测试
- 需要重构现有代码但缺乏测试覆盖
- 对代码正确性要求极高的场景(金融、安全、基础设施)
- 团队希望建立可回归的测试套件
核心规则
- 测试先行:在写任何实现代码之前,先写一个会失败的测试。没有测试就没有实现。
- 最小实现:只写刚好让测试通过的代码,不过度设计。YAGNI 原则适用于每一步。
- 每次只改一件事:一个测试循环只关注一个行为变化,不混合多个改动。
- 重构在绿灯之后:只有所有测试通过时才能重构。红条状态下禁止重构。
- 保持测试独立:每个测试不依赖其他测试的执行顺序,可以单独运行。
工作流程
-
红阶段 — 写测试
- 明确要实现的单个行为或功能
- 编写一个简洁的测试用例,描述期望的输入和输出
- 运行测试,确认它失败(且失败原因符合预期——期望的功能尚未实现)
- 如果测试在实现前就通过了,说明测试没有有效验证目标行为,需要修正
-
绿阶段 — 最小实现
- 编写刚好够让测试通过的代码,不追求优雅或通用
- 可以使用硬编码、复制粘贴等快速手段
- 运行测试确认通过
- 目标是最短时间进入绿灯状态
-
重构阶段 — 改善设计
- 在绿灯保护下,消除重复代码、提取抽象、改善命名
- 每次只做一个小重构,频繁运行测试确保不引入回归
- 可进行的重构:提取方法、重命名变量、消除魔法数字、引入设计模式
- 测试本身也需要重构:消除重复的 setup、提取公共夹具
-
循环迭代
- 重复上述三步,逐步构建完整功能
- 每次循环增加一个测试,覆盖一个新的行为分支
- 当所有需求都被测试覆盖时,功能完成
测试金字塔
/ E2E \
/ 集成测试 \
/------------\
/ 单元测试 \
/----------------\
- 单元测试(最底层,最多):测试单个函数或方法,快速、稳定、精确。应占测试总量的约 70%。
- 集成测试(中间层):测试模块间交互、数据库操作、API 调用。约占 20%。
- 端到端测试(顶层,最少):模拟用户操作全流程。最慢、最脆弱,仅覆盖关键路径。约占 10%。
AAA 模式
每个测试用例遵循 Arrange-Act-Assert 三段结构:
- Arrange(准备):创建测试所需的对象、数据和依赖。设置 mock/stub。
- Act(执行):调用被测试的方法或函数,通常只有一行。
- Assert(断言):验证执行结果是否符合预期。每个测试聚焦一个行为,断言不宜过多。
测试策略
- 边界值测试:空值、零值、最大值、最小值、刚好超界的值
- 等价类划分:将输入空间划分为若干等价类,每类取一个代表值
- 异常路径测试:验证错误处理逻辑是否按预期工作
- 属性测试:定义输入应满足的不变性质,用随机生成的数据验证
常见反模式
- 先写实现再补测试:这不是 TDD。事后补的测试往往只验证已知路径,遗漏边界和异常。
- 测试过于庞大:一个测试包含多个 Arrange-Act-Assert 块,应从中间分离。
- 紧耦合测试:测试依赖内部实现细节而非公共行为,重构后大面积失败。
- 没有断言:测试运行了但没有验证任何结果,形同虚设。
- 依赖外部资源:测试依赖数据库连接、网络请求或文件系统,导致不稳定。
参考标准
- Kent Beck《测试驱动开发》—— TDD 奠基性著作
- Martin Fowler《重构》—— 重构手法参考
- IEEE 829 测试文档标准
- xUnit 测试模式(Setup/Teardown、Fixture、Mock/Stub/Fake 区分)
- FIRST 原则:Fast(快速)、Independent(独立)、Repeatable(可重复)、Self-Validating(自验证)、Timely(及时)
- 测试覆盖率工具:按语句、分支、函数、行多维度度量,关注趋势而非绝对值
- 测试命名规范:测试名称应描述被测试的行为和期望结果,而非实现细节