一、什么是 TDD
TDD (Test‑Driven Development,测试驱动开发) 核心思想:先写测试,再写业务代码,让测试来驱动功能实现,和传统思路反过来。
传统开发:写业务代码 → 写完再写测试 TDD 开发:先写测试用例 (现在一定会报错) → 写最少代码让测试通过 → 重构优化
二、TDD 开发三步法
- 红 (Red) :编写一个失败的单元测试。此时功能代码还不存在 / 不完善,运行测试直接报错。
- 绿 (Green) :编写最少、最简单的业务代码,让这条测试刚好通过,不追求优雅。
- 重构 (Refactor) :优化代码结构、消除重复,保证所有测试依然全部通过。
循环往复,每一个小功能都走一遍红‑绿‑重构。
TDD 不是为了多写测试;是用测试来定义需求。
三、TDD 优点 & 缺点
优点
- 代码天然可测试,高内聚低耦合
- 需求通过测试用例显性表达,减少理解偏差
- 修改、重构时快速回归,防止改坏原有功能
- 自动形成一套完整单元测试集
- 逼你从小粒度、简单功能开始实现
缺点
- 前期速度慢,要花时间设计测试
- 不适合超复杂算法、需求极不稳定频繁大改场景
- 需要开发者熟练单元测试框架
- 只能保证 "代码满足测试用例",不能保证覆盖全部业务场景
四、Demo:金额折扣工具
需求:
- 原价大于等于 100,打 9 折
- 原价小于 100,不打折
TDD 流程演示
1.Red:先写测试
java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class DiscountUtilTest {
//测试满100打9折
@Test
void calcDiscount_priceOver100(){
DiscountUtil util = new DiscountUtil();
double pay = util.calc(200);
assertEquals(180,pay,0.001);
}
//小于100不打折
@Test
void calcDiscount_priceLess100(){
DiscountUtil util = new DiscountUtil();
double pay = util.calc(50);
assertEquals(50,pay,0.001);
}
}
🔴报错,DiscountUtil 不存在
2.Green 最简实现先过第一个测试
java
public class DiscountUtil {
public double calc(double price) {
return 180;
}
}
第一条测试过,第二条失败,继续完善
3.完善代码,全部用例通过
java
public class DiscountUtil {
public double calc(double price) {
if(price >=100){
return price * 0.9;
}
return price;
}
}
✅全部测试通过,再做重构,比如抽常量折扣率
java
public class DiscountUtil {
private static final double DISCOUNT_RATE = 0.9;
public double calc(double price) {
if(price >=100){
return price * DISCOUNT_RATE;
}
return price;
}
}
五、TDD 和普通单元测试区别
| TDD | 开发后补单元测试 | |
|---|---|---|
| 顺序 | 测试先行 | 业务代码先行 |
| 作用 | 驱动设计、定义需求、反馈 | 验证已完成代码正确性 |
| 时机 | 写功能前 | 写完功能以后 |
| 设计影响 | 倒逼接口设计简单易测 | 很难改变已经写好的糟糕设计 |