TDD 测试驱动开发

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?

  1. 挑一个小功能:比如一个校验函数、一个工具方法

  2. 选一个测试框架:Jest(JS)、pytest(Python)、JUnit(Java)、RSpec(Ruby)

  3. 强迫自己"先写测试":哪怕只是一个断言

  4. 坚持一个周末:你会发现思维方式变了


最后说一句

TDD 不是一个技术问题,而是一个习惯问题。一开始你会觉得别扭,像用左手写字。但只要坚持二三十个循环,你会开始享受那种"写代码不怕改"的踏实感。

写测试不是为了发现 bug,而是为了让你根本没有机会产生 bug。

相关推荐
心易行者23 天前
AI Coding 从“抽盲盒”到“开火箭”:SDD+TDD 开发模式实战揭秘
人工智能·tdd
Max_uuc1 个月前
【工程心法】从“在板盲调”到“云端验证”:嵌入式单元测试与 TDD 的工程化革命
单元测试·tdd
Max_uuc2 个月前
【C++ 硬核】摆脱开发板:用 Google Test + Mock 构建嵌入式 TDD (测试驱动开发) 体系
驱动开发·tdd
Coder_Boy_2 个月前
基于SpringAI的在线考试系统-数据库设计核心业务方案
java·数据库·spring boot·ddd·tdd
Coder_Boy_3 个月前
基于SpringAI的在线考试系统-智能考试系统-学习分析模块
java·开发语言·数据库·spring boot·ddd·tdd
Coder_Boy_3 个月前
基于SpringAI的在线考试系统-阅卷评分与错题管理模块回归测试逻辑梳理文档
java·spring boot·系统架构·ddd·tdd·全栈开发
Coder_Boy_3 个月前
基于SpringAI的在线考试系统-考试管理功能布局+交互优化方案
java·数据库·人工智能·spring boot·交互·ddd·tdd
Coder_Boy_3 个月前
基于SpringAI的在线考试系统-0到1全流程研发:DDD、TDD与CICD协同实践
java·人工智能·spring boot·架构·ddd·tdd
Coder_Boy_3 个月前
基于SpringAI的在线考试系统-DDD(领域驱动设计)核心概念及落地架构全总结
java·大数据·人工智能·spring boot·架构·ddd·tdd