一、TDD基础:什么是测试驱动开发
说实话,我第一次接触TDD时,心里是抗拒的。
“先写测试再写代码?这不是反人类吗?”我当时就是这么想的。
但后来在一个React项目中,我被bug追着跑了三个月。每次改一个功能,就冒出两个新bug。那时候我才意识到——嗯,也许TDD真有点东西。
1.1 什么是测试驱动开发
测试驱动开发,英文叫Test-Driven Development,简称TDD。
说白了,就是先写测试,再写代码。不是写完代码再补测试,而是反过来。
你想想看,这就像盖房子之前先画图纸。没有图纸就开工,最后大概率要返工。
TDD的核心思想:用测试来驱动代码的设计和实现,而不是用代码来驱动测试的编写。
我在项目中遇到过很多团队,他们所谓的“测试”就是上线前手动点一遍。结果呢?每次发布都像在赌博。
1.2 TDD的红-绿-重构循环
TDD有三个步骤,我习惯叫它“红-绿-重构”循环。听起来像交通信号灯?没错,就是这个意思。
- 红灯阶段(Red):先写一个会失败的测试
- 绿灯阶段(Green):写最少的代码让测试通过
- 重构阶段(Refactor):优化代码,保持测试通过
这三个步骤不断重复,就像呼吸一样自然。
我的小技巧:在红灯阶段,我通常会先想清楚“这个功能到底要做什么”。测试写清楚了,代码怎么写就清晰了。
举个例子,假设我们要写一个加法函数:
// 红灯阶段:先写测试
test('add(1, 2) 应该返回 3', () => {
expect(add(1, 2)).toBe(3);
});
// 绿灯阶段:写最简代码
function add(a, b) {
return a + b;
}
// 重构阶段:优化(这里已经很简单了,不需要重构)
你可能会说:“这也太简单了吧?”没错,TDD就是从这种小步快跑开始的。
1.3 为什么要在React中使用TDD
这个问题我问过自己很多次。尤其是在React项目中,组件交互复杂,状态管理混乱,测试真的有必要吗?
我的答案是:越复杂,越需要TDD。
| 场景 | 没有TDD | 有TDD |
|---|---|---|
| 修改一个组件 | 担心影响其他组件 | 测试会告诉你哪里出问题 |
| 新增功能 | 手动测试所有相关页面 | 跑一遍测试就知道 |
| 重构代码 | 不敢动,怕改出bug | 放心重构,测试兜底 |
| 团队协作 | 每个人改自己的,互不信任 | 测试就是契约,大家都放心 |
我曾经在一个React项目中,接手了一个没有测试的代码库。改一个按钮样式,结果把整个表单提交逻辑搞崩了。你猜怎么着?上线后用户投诉,我们花了三天才定位到问题。
避坑指南:我曾经以为“小项目不需要测试”,结果小项目变成了大项目,测试成本翻了十倍。记住:测试不是成本,是投资。
1.4 React中TDD的独特优势
React的组件化特性,让TDD变得特别顺手。
- 组件即函数:给定props,返回UI。测试起来非常直接
- 状态管理可预测:用React Testing Library模拟用户操作,验证状态变化
- Hooks可单独测试:自定义hooks可以脱离组件单独测试
举个例子,测试一个简单的计数器组件:
// 红灯:先写测试
test('点击按钮后计数增加', () => {
render(<Counter />);
const button = screen.getByText('+1');
fireEvent.click(button);
expect(screen.getByText('计数: 1')).toBeInTheDocument();
});
// 绿灯:实现组件
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>计数: {count}</p>
<button onClick={() => setCount(c => c + 1)}>+1</button>
</div>
);
}
你看,测试写完了,组件怎么设计也就清楚了。这就是TDD的魅力——测试驱动设计。
1.5 什么时候不适合用TDD
说实话,TDD不是万能的。我也不会在所有场景都用它。
- 原型开发:快速验证想法时,先写代码再补测试
- 探索性编程:不确定怎么实现时,先写代码试试
- UI动画:视觉效果的测试比较难写,可以适当放宽
但记住:生产代码一定要有测试。这是底线。
我的建议:刚开始学TDD时,不要追求100%覆盖率。从核心逻辑开始,慢慢养成习惯。我花了大概两个月才真正适应这种开发方式。
好了,这一章我们聊了TDD的基础概念。下一章,我们会深入React Testing Library,看看怎么在React项目中真正落地TDD。
记住:先写测试,再写代码。这不是浪费时间,是在为你未来的自己铺路。