Download the exact captured file
---
name: shared/tdd
description: 测试驱动开发(TDD)方法论,Red-Green-Refactor 循环,先写失败测试再写实现
version: 1.0.0
agent: shared
type: methodology
user-invocable: false
agent-invocable: true
dependencies: []
triggers:
- 实现新功能前
- 修复 bug 前
- 重构代码前
- 任何行为变更前
metadata:
internal: true
---
# 测试驱动开发(TDD)
## 核心铁律
```
没有失败的测试,就不写生产代码。
```
如果先写了实现代码,**必须删除**,从测试重新开始。不保留"参考",不"边写测试边改"。
## 适用场景
**必须使用 TDD**:
- 新功能实现
- Bug 修复
- 代码重构
- 任何行为变更
**例外(需用户明确同意)**:
- 一次性原型代码
- 纯配置文件
- 自动生成的代码
## Red-Green-Refactor 循环
### 🔴 RED — 写失败测试
为**一个行为**写最小测试:
- 一个测试只测一件事(名称里有 "and" 就拆开)
- 名称描述行为,而非实现(`rejects empty email` 而非 `test1`)
- 用真实代码,除非依赖外部服务才 mock
- 展示你期望的 API 长什么样
```typescript
// ✅ 好:测试真实行为
test('rejects empty email', () => {
expect(validateEmail('')).toBe(false);
});
// ❌ 坏:测试 mock 而非代码
test('email works', () => {
const mock = jest.fn();
mock('');
expect(mock).toHaveBeenCalled();
});
```
### 验证 RED — 必须看到它失败
**这一步不可跳过。** 运行测试,确认:
```bash
# 前端
npx jest path/to/test.test.ts
# 后端
npm test -- path/to/test.test.ts
```
确认三件事:
1. 测试**失败**(不是通过)
2. 失败原因是**功能缺失**(不是拼写错误、导入错误)
3. 失败信息符合预期
**如果测试直接通过** → 你在测已有行为,测试写错了,修正它。
**如果测试报错(error)** → 修复错误(如导入路径),重跑直到它正确失败。
### 🟢 GREEN — 写最小实现
写**刚好让测试通过**的最少代码:
- 不添加测试未要求的功能
- 不重构、不"顺便优化"
- 不提前抽象
```typescript
// ✅ 好:刚好通过
function validateEmail(email: string): boolean {
return email.trim().length > 0;
}
// ❌ 坏:过度工程
function validateEmail(
email: string,
options?: { allowPlus?: boolean; domainWhitelist?: string[] },
): boolean {
// YAGNI
}
```
### 验证 GREEN — 必须看到它通过
运行测试,确认:
1. 新测试通过
2. **所有已有测试仍然通过**
3. 输出干净(无报错、无意外警告)
**如果新测试失败** → 修代码,不修测试。
**如果其他测试失败** → 立即修复。
### 🔵 REFACTOR — 重构(仅在 GREEN 之后)
测试全绿后才允许重构:
- 消除重复
- 改善命名
- 提取辅助函数
- **不新增行为**
重构后必须重新跑全部测试,保持全绿。
### 循环
下一个行为 → 回到 RED。
## Bug 修复 TDD
修复 bug 时,先写一个**复现 bug 的失败测试**:
```
1. 写测试复现 bug → 确认失败(RED)
2. 修复代码 → 确认通过(GREEN)
3. 这个测试就是回归保护
```
**禁止**在没有复现测试的情况下修复 bug。
## 验证清单
标记任务完成前,逐项确认:
- [ ] 每个新增函数/方法都有对应测试
- [ ] 每个测试都先看到它失败(RED 已验证)
- [ ] 失败原因是功能缺失,而非拼写/导入错误
- [ ] 实现代码是让测试通过的最小集
- [ ] 全部测试通过
- [ ] 测试用真实代码(仅在不可避免时 mock)
- [ ] 边界条件和错误路径已覆盖
**任何一项打不了勾 → 你跳过了 TDD,回到 RED 重新开始。**
## 常见借口与回应
| 借口 | 回应 |
| ------------------------ | ------------------------------------ |
| "太简单了不用测" | 简单代码也会坏,测试只要 30 秒 |
| "我之后补测试" | 事后写的测试一跑就过,证明不了任何事 |
| "我手动测过了" | 手动测试无记录、不可复跑、容易漏 |
| "已经写了代码,删了浪费" | 沉没成本。保留未验证的代码才是技术债 |
| "TDD 太慢" | TDD 比事后调试快,省的是未来的时间 |
| "这个不一样因为..." | 没有例外。删代码,从测试开始。 |
## 卡住时
| 问题 | 解法 |
| ------------- | ------------------------------- |
| 不知道怎么测 | 先写你期望的 API 调用,再写断言 |
| 测试太复杂 | 说明设计太复杂,简化接口 |
| 什么都要 mock | 代码耦合太高,用依赖注入解耦 |
| 测试设置庞大 | 提取辅助函数;仍复杂就简化设计 |