tdd
产品介绍
tdd 是 Matt Pocock Skills 中「修 bug 流水线」的第 3 步,是一个测试驱动开发(Test-Driven Development)Skill。安装在 ~/.trae-cn/skills/tdd/SKILL.md,当用户说「测试驱动开发」时 AI 自动调用。核心是红-绿-重构循环。
什么是 TDD
TDD 不是「先写代码,事后补测试」。它的思路正好相反:
传统开发:需求 → 设计 → 写代码 → 手动测试 → 补自动化测试 TDD:需求 → 写测试(测试先失败)→ 写代码让测试通过 → 重构
TDD 的核心洞察是:先写测试,你才知道自己要写什么代码。就像先写一份验收标准,再按标准实现。
红-绿-重构循环
这是 TDD 的三个阶段,不断循环:
红(Red)
做什么:写一个会失败的测试。
为什么先让它失败:如果测试一开始就通过,说明它没测什么需要测的东西,或者测的东西已经存在了。失败的测试证明:测试真的在测你想测的东西。
完成标准:测试运行,失败,失败信息清晰。
例子:
test('除以零应该抛出错误', () => {
expect(() => divide(10, 0)).toThrow();
});
// 运行 → 红:divide 函数还不存在,或者没有抛出错误
绿(Green)
做什么:写最少的代码让测试通过。
关键:是「最少」的代码,不是「最好」的代码。
为什么最少:这个阶段的目标是让测试通过,不是完美实现。写得越少,越不容易引入不必要的复杂度。完美主义在重构阶段再来。
完成标准:测试运行,通过。
例子:
// 最少代码让上面的测试通过
function divide(a, b) {
if (b === 0) throw new Error('Cannot divide by zero');
return a / b;
}
// 运行 → 绿:测试通过
重构(Refactor)
做什么:优化代码结构,保持测试通过。
为什么:刚写的代码通常很粗糙(一个 if-else 就搞定了),但这样的代码久了会难以维护。重构让它保持可读性和可维护性,同时测试保证你不会破坏功能。
完成标准:测试仍然全部通过,代码更清晰。
例子:
// 重构前
function divide(a, b) {
if (b === 0) throw new Error('Cannot divide by zero');
return a / b;
}
// 重构后(提取错误处理,分离关注点)
function divide(a, b) {
validateNotZero(b);
return a / b;
}
function validateNotZero(value) {
if (value === 0) throw new Error('Cannot divide by zero');
}
核心原则
1. 垂直切片
一次只做一个功能的一小部分,不要一次写完所有测试。
为什么:垂直切片让你每次循环都能得到一个可运行的、端到端的成果。水平切片(先写完所有测试再写实现)会让你在很长时间内看不到任何成果,而且容易产生垃圾测试。
反例(水平切片):
写用户登录的所有测试 → 写用户登录的所有代码 → 运行
(中间有很长一段时间测试全是红的,容易放弃)
正例(垂直切片):
写「用户名为空」的测试 → 写代码让测试通过 → 重构
写「密码为空」的测试 → 写代码让测试通过 → 重构
写「用户名密码都正确」的测试 → 写代码让测试通过 → 重构
(每次循环都有一个能跑的版本)
2. 测试行为,不测试实现
测试应该描述系统「做什么」,而不是「怎么做」。
为什么:测试实现细节会导致重构时测试频繁挂掉。你重构就是为了改实现,如果测试绑在实现上,重构就举步维艰。
反例(测试实现):
test('应该调用 saveToLocalStorage', () => {
// mock 内部函数,验证它被调用了
});
// 重构:换一种存储方式 → 测试挂了 → 重构失败
正例(测试行为):
test('用户数据应该被保存', () => {
// 保存用户数据
saveUser(userData);
// 验证数据能被读回来
expect(readUser()).toEqual(userData);
});
// 重构:换一种存储方式 → 测试仍然通过
3. 永远不要在红的时候重构
先让测试通过(绿),再优化代码(重构)。
为什么:在测试失败时重构,你不知道是代码有问题还是测试有问题。先变绿,说明测试和代码达成一致,这时重构才是安全的。
反模式
水平切片
先写完所有测试,再写完所有实现。
问题:
- 长时间没有成果,容易放弃
- 测试容易变成「为了写测试而写测试」,没有实际价值
- 发现问题时已经写了很多代码,改动成本高
测试实现细节
测试内部函数、mock 内部协作者。
问题:
- 重构时测试会挂
- 测试变得脆弱,频繁误报失败
- 团队开始忽视测试
假绿
测试看起来通过了,但实际上什么都没测。
常见原因:
- mock 了太多东西,测试的不是真实行为
- 断言太弱(比如只断言函数不抛错)
- 测试数据是假的,和真实场景无关
使用场景
- 修 bug 时,先写回归测试:diagnosing-bugs 第 5 步就是这个
- 开发新功能时,先写测试再写实现
- 重构旧代码时,先写测试保护现有行为
与上下游的关系
- 上游:diagnosing-bugs 诊断循环的第 5 步(修 + 回归测试)用的就是 TDD 方法
- 本身:TDD 循环可以反复迭代,每次循环产出一个可运行的小版本
相关笔记
- Matt Pocock Skills - Skill 集合
- triage - 上一步:问题分流
- diagnosing-bugs - 上一步:诊断循环
相关日记
- 2026-08-26 - 安装当天