TDD 是啥?一篇让你彻底搞懂的博客
别再迷信"写完再测"了,试试把测试写在前面
你有没有遇到过这种情况:
-
吭哧吭哧写了一千行代码,运行起来全是红字,debug 到凌晨三点
-
改了一行代码,某个看起来毫不相关的功能突然挂了
-
项目上线前疯狂补测试,发现根本补不动
如果你点头了,那今天聊的 TDD 很可能就是你的救星。
一、TDD 是什么?
TDD 全称 Test-Driven Development ,中文叫测试驱动开发。
名字听着挺玄乎,但核心思想就一句话:
先写测试,再写代码。
对,你没看错。不是写完代码顺便测一下,也不是写完再补测试,而是测试代码比业务代码先出生。
TDD 不是什么高深理论,而是一套极其简单、近乎偏执的流程。它由 Kent Beck 在 1990 年代末提出,随着极限编程(XP)和敏捷开发火遍全球。
二、TDD 的"红-绿-重构"三步曲
TDD 的日常就是循环做三件事,像呼吸一样自然:
🔴 第一步:红 ------ 写一个失败的测试
先想清楚你要实现什么功能,然后用测试代码表达出来。这时候功能代码还没写,所以测试必然失败。测试工具会给你一个红色的结果。
javascript
// 需求:写一个加法函数 add(a, b)
// 先写测试
test('add(1, 2) 应该等于 3', () => {
expect(add(1, 2)).toBe(3)
})
运行 → 红(因为 add 函数根本不存在)
🟢 第二步:绿 ------ 写最少代码让测试通过
写刚好能让测试通过的业务代码。不要多想,不要过度设计,不要写额外功能。目标只有一个:从红变绿。
javascript
function add(a, b) {
return 3 // 最偷懒的写法,但测试确实能过
}
运行 → 绿
🔵 第三步:重构 ------ 优化代码,保持绿色
现在测试是绿的,你可以放心地整理代码:消除重复、改个好名字、提取变量。只要测试依然全绿,你的修改就是安全的。
javascript
function add(a, b) {
return a + b // 重构为通用实现
}
运行 → 还是绿
然后重复这个循环,一个功能一个功能地迭代。
三、为什么非要先写测试?三个核心理由
1. 让你真正"想清楚再动手"
写测试的过程,就是在定义"完成"的标准。当你把输入输出、边界条件都写成测试时,需求模糊的地方会自己暴露出来。
很多时候写着写着就会发现:"等等,这里的需求我没搞明白。"------这比写完整段代码才发现要好一万倍。
2. 安全感爆棚,改代码不再手抖
有了 TDD,你的代码会被一张测试网兜住。随便重构、大胆加功能,只要测试跑过,心里就有底。
很多团队不敢重构老代码,就是因为没有测试。TDD 正好解决了这个死结。
3. 自动生成文档,新人秒懂
测试就是最好的文档。它不说废话,直接告诉你"这个函数应该返回什么,不应该返回什么"。新人看一遍测试,比读十行注释都管用。
四、一个完整的 TDD 小例子(JavaScript)
假设我们要写一个函数 isAdult(age),判断年龄是否成年(≥18)。
循环1:基础功能
🔴 先写测试
javascript
test('18 岁应该返回 true', () => {
expect(isAdult(18)).toBe(true)
})
🟢 写最少代码
javascript
function isAdult(age) {
return true
}
🔵 重构(暂时没啥可重构的)
循环2:增加边界
🔴 写第二个测试
javascript
test('17 岁应该返回 false', () => {
expect(isAdult(17)).toBe(false)
})
运行 → 红(因为函数永远返回 true)
🟢 改代码让它都通过
javascript
function isAdult(age) {
return age >= 18
}
运行 → 绿
🔵 重构(代码已经很简单,跳过)
循环3:处理异常
🔴 写第三个测试
javascript
test('负数年龄应该抛出错误', () => {
expect(() => isAdult(-5)).toThrow('年龄不能为负数')
})
运行 → 红(目前没有抛错)
🟢 实现
javascript
function isAdult(age) {
if (age < 0) throw new Error('年龄不能为负数')
return age >= 18
}
运行 → 绿
🔵 重构(结束)
你看,整个过程非常克制:测试驱动着代码一点点长出来,没有提前写任何多余的东西。
五、TDD 常见误区(新手必看)
| 误区 | 真相 |
|---|---|
| TDD 就是写单元测试 | 单元测试只是工具,TDD 是一种开发流程 |
| 先写测试太浪费时间 | 前期慢一点,后期省下 10 倍 debug 时间 |
| 所有代码都要 TDD | UI、数据库、并发代码不适合强 TDD,可以选关键逻辑做 |
| 一次写一大堆测试 | 一次只写一个最小测试,红→绿→重构,步子迈小 |
六、TDD 不是银弹,但值得一试
TDD 并不适合所有场景:
-
原型探索:需求极度不确定时,先写代码探路可能更高效
-
简单脚本:一次性的数据处理脚本,写测试有点重
-
UI 频繁变动:界面变化太快,测试跟着改会很痛苦
但如果你在维护一个长期项目 、开发核心业务逻辑 、或者受够了改代码就出 bug 的折磨,TDD 绝对值得认真学一学。
七、如何开始你的第一个 TDD?
-
挑一个小功能:比如一个校验函数、一个工具方法
-
选一个测试框架:Jest(JS)、pytest(Python)、JUnit(Java)、RSpec(Ruby)
-
强迫自己"先写测试":哪怕只是一个断言
-
坚持一个周末:你会发现思维方式变了
最后说一句
TDD 不是一个技术问题,而是一个习惯问题。一开始你会觉得别扭,像用左手写字。但只要坚持二三十个循环,你会开始享受那种"写代码不怕改"的踏实感。
写测试不是为了发现 bug,而是为了让你根本没有机会产生 bug。