---
name: tdd-master
description: "Use when 用户要实现新功能、修复 bug,或明确要求使用 TDD 方式开发、先写测试时。触发场景:tdd、测试驱动、测试驱动开发、先写测试、红绿重构、单元测试、test driven、TDD开发、用TDD写、测试先行、我要开始开发新功能。"
---
# TDD 开发大师
铁律:**没有失败的测试,不写一行生产代码。** 先写代码再补测试的,必须删掉先写的代码重来。
禁止在以下情况下编写任何生产代码:
1. 还没有一个因"正确原因"而失败的测试(RED 阶段未完成)
2. 用户还没有确认接口设计和行为清单(阶段一未完成)
这两条无一例外,"简单功能"也不例外。
## 核心哲学
### 竖向切片,而非横向切片
**反模式(横向切片)**:先把所有测试写完,再一口气写所有实现
- 问题:测试套件成为规格书,不是活文档;实现阶段难以获得快速反馈
**正确做法(竖向 tracer bullet 切片)**:每次选一个最小可验证行为,完成 RED→GREEN→REFACTOR 完整循环
- 每个切片都是端到端的最小功能(一个完整行为)
- 通过测试可以立即运行并得到结果
### 测试测行为,而非测实现
```python
# 错误:测试内部实现(脆弱,重构即失效)
def test_calls_validate_method():
service = UserService()
with patch.object(service, '_validate') as mock:
service.create_user(data)
mock.assert_called_once()
# 正确:测试可观察行为(稳健,重构不影响)
def test_create_user_returns_user_id():
service = UserService()
user_id = service.create_user({"name": "Alice", "email": "a@example.com"})
assert isinstance(user_id, int)
assert user_id > 0
```
---
## 工作流
### 阶段一:规划(获得用户批准前禁止写代码)
#### 1.1 接口设计
先设计公共接口,不暴露内部实现细节:
```
询问用户:这个功能/模块需要提供什么公共接口?
输出:函数/方法签名 + 输入/输出类型 + 前置/后置条件
```
加载 `references/testing-principles.md` 检查接口设计原则。
#### 1.2 行为清单
将功能拆解为可测试的行为列表:
```
待实现的行为:
- [ ] 正常路径:[描述]
- [ ] 边界条件:[描述]
- [ ] 错误路径:[描述]
- [ ] 并发场景:[如适用]
```
每个行为必须是:**独立可测试** + **有明确期望结果** + **最小粒度**
#### 1.3 可测试性检查
评估设计是否可测试(加载 `references/testing-principles.md`):
- 依赖是否可以被替换(Mock/Stub)?
- 是否有隐藏的全局状态?
- 是否混合了业务逻辑和 I/O?
**将设计展示给用户确认,批准后才开始实现。**
---
### 阶段二:RED-GREEN-REFACTOR 循环
每次选一个行为(从行为清单第一项开始):
#### RED 阶段
1. **写最小的失败测试**:
- 测试名称描述行为(`test_用户注册成功返回用户ID`)
- 只测一个行为
- 使用尽可能真实的代码(避免过度 mock)
2. **强制验证 RED**(不可跳过):
必须在当前消息中实际运行测试命令并贴出失败输出。不得说"测试会失败"而不运行。看到测试失败的输出是进入 GREEN 阶段的唯一门票。
```bash
pytest tests/test_user.py::test_用户注册成功返回用户ID -v
```
确认:测试因**正确原因**失败(功能未实现),而非因测试代码错误失败
3. **RED 失败则停止**:如果无法让测试变红,说明测试本身有问题,先修复测试
#### GREEN 阶段
1. **写最小的实现**:只写让当前测试通过所需的最少代码
- 可以暂时硬编码(如 `return 42`),只要测试通过
- 禁止超前实现"以后会用到的"功能
2. **强制验证 GREEN**(不可跳过):
```bash
pytest tests/test_user.py -v
```
确认:全部测试通过,包括之前的测试
3. **GREEN 失败则停止**:回到实现代码修复,**不重构,不写新测试**
#### REFACTOR 阶段
**只在全绿时重构**,RED 状态下严禁重构。
加载 `references/refactoring-guide.md` 进行安全重构:
- 消除重复代码
- 改善命名
- 提取方法(不改变外部行为)
每次重构后立即运行全部测试:全绿则继续,失败则撤销上一步修改。
#### 完成一个行为后
勾选清单,选下一个行为,重复循环。
---
### 阶段三:深模块设计检查
所有行为实现完成后,加载 `references/deep-modules.md`:
- 接口是否足够简单(浅接口是警告信号)?
- 是否可以进一步内化复杂性?
- 模块边界是否清晰?
---
## Mock 使用原则
加载 `references/mocking-guide.md` 获取详细指引。
**何时使用 Mock**:
- 外部 I/O(网络请求、数据库、文件系统)
- 时间相关(`datetime.now()`、`time.sleep()`)
- 随机数
- 第三方付费 API
**何时不用 Mock**:
- 自己写的代码(测试真实集成)
- 简单的值对象
- 不涉及副作用的纯函数
---
## 反模式警告
| 反模式 | 识别特征 | 处理方式 |
|--------|----------|----------|
| 横向切片 | "先把所有测试写完" | 立刻停止,改为逐行为切片 |
| 测试实现细节 | `patch.object` 内部方法 | 重写为测试行为 |
| 巨型测试 | 一个测试验证多个行为 | 拆分为多个测试 |
| 跳过 RED 验证 | "我知道测试会失败" | 必须执行,有时会有惊喜 |
| GREEN 阶段重构 | 测试刚过就大改 | 先让所有测试通过再重构 |
| 过度 Mock | Mock 了自己写的类 | 测试真实集成 |
| 无意义测试名 | `test_it_works` | 改为行为描述 |
---
## 警告:当你想跳过 TDD 流程时
遇到以下想法,立刻停下——这些是你即将违反铁律的信号:
| 借口 | 现实 |
|------|------|
| "这个功能太简单,不需要先写测试" | 简单功能的边界条件往往出错。TDD 的价值在设计接口,不只是测试。 |
| "先写代码,写完后补测试效果一样" | 补测试 = 测"代码做了什么",不是测"代码应该做什么"。本质不同。 |
| "我已经在心里验证过了,测试只是形式" | 心理验证不算验证。必须运行命令并看到红色输出。 |
| "用户在等,没时间写测试" | 没有测试的代码往往需要返工。TDD 总体耗时更短。 |
| "这是修复 bug,不需要走 TDD" | 修 bug 时先写能复现 bug 的测试,是最重要的 TDD 应用场景之一。 |
| "我只是稍微改了一点现有代码" | "稍微改" 没有测试保护 = 引入回归 bug 的标准路径。 |
| "RED 阶段测试肯定会失败,没必要运行" | "肯定会失败"的测试有时不会失败——这意味着测试本身有问题。必须运行。 |
**这些借口的本质都是同一件事:你在合理化绕过 TDD。删掉先写的代码,重来。**
## 参考资源
- `references/testing-principles.md` — 测试原则与可测试性设计
- `references/mocking-guide.md` — Mock 使用指南
- `references/refactoring-guide.md` — 安全重构技术
- `references/deep-modules.md` — 深模块设计原则