# Tdd

> 红-绿-重构循环的测试驱动开发工作流。适用于需用 TDD 构建功能、修复 bug、编写集成测试，或提到"红绿重构"时的场景。

- Skill: `yulin-bi/tdd` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add yulin-bi/tdd`
- Raw SKILL.md: https://api.skillmd.com/api/skills/yulin-bi/tdd/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Yulin-Bi (https://skillmd.com/u/yulin-bi)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/yulin-bi/tdd

---


# 测试驱动开发

## 核心理念

**核心原则**：测试应通过公共接口验证行为，而非验证实现细节。代码可以彻底重构，但测试不应随之变动。

**好测试**是集成风格的：它们通过公共 API 执行真实的代码路径。测试描述的是系统**做什么**，而不是**怎么做**。好测试读起来就像一份规范——"用户可以用有效的购物车完成结账"——清楚说明了系统具备什么能力。这类测试能够经受住重构，因为它们不关心内部结构。

**坏测试**与实现紧密耦合。它们模拟（mock）内部协作者、测试私有方法，或者通过外部手段间接验证（例如不通过接口而是直接查询数据库）。警告信号：当你重构时测试挂了，但行为没有变化。如果你重命名了一个内部函数而导致测试失败，那些测试测试的是实现，而非行为。

详见 [tests.md](tests.md) 中的示例和 [mocking.md](mocking.md) 中的模拟指南。

## 反模式：水平切分

**不要**先写完所有测试，再写所有实现。这就是"水平切分"——把 RED 阶段理解为"写所有测试"，GREEN 阶段理解为"写所有代码"。

这会产生**糟糕的测试**：

- 批量编写的测试测试的是**想象中**的行为，而非**实际**的行为
- 你最终测试的是事物的**形状**（数据结构、函数签名），而非面向用户的行为
- 测试对真实变更变得不敏感——行为被破坏时测试通过，行为正常时测试反而失败
- 你超出了自己的视野范围，在还没理解实现之前就锁定了测试结构

**正确做法**：通过"示踪子弹"（tracer bullet）垂直切分。一个测试 → 一个实现 → 重复。每个测试都基于前一个周期学到的经验。因为你刚刚写了代码，你完全知道什么行为是重要的以及如何验证它。

```
错误（水平切分）：
  RED:   test1, test2, test3, test4, test5
  GREEN: impl1, impl2, impl3, impl4, impl5

正确（垂直切分）：
  RED→GREEN: test1→impl1
  RED→GREEN: test2→impl2
  RED→GREEN: test3→impl3
  ...
```

## 工作流程

### 1. 规划

在探索代码库时，使用项目的领域术语，使测试名称和接口词汇与项目语言保持一致，并遵守相关区域的 ADR。

在编写任何代码之前：

- [ ] 与用户确认需要哪些接口变更
- [ ] 与用户确认要测试哪些行为（确定优先级）
- [ ] 识别机会来设计[深度模块](deep-modules.md)（小接口、深实现）
- [ ] 设计[可测试性](interface-design.md)良好的接口
- [ ] 列出要测试的行为（不是实现步骤）
- [ ] 获得用户对计划的批准

提问："公共接口应该是什么样子？哪些行为最重要需要测试？"

**你不可能测试所有东西。** 与用户确认究竟哪些行为最重要。将测试精力集中在关键路径和复杂逻辑上，而非每个可能的边缘情况。

### 2. 示踪子弹

编写一个测试，验证系统的一件事情：

```
RED:   编写第一个行为的测试 → 测试失败
GREEN: 编写最简代码使其通过 → 测试通过
```

这是你的示踪子弹——证明端到端的路径是通的。

### 3. 增量循环

对剩余每个行为：

```
RED:   编写下一个测试 → 失败
GREEN: 编写最简代码使其通过 → 通过
```

规则：

- 一次只写一个测试
- 只写刚好能让当前测试通过的代码
- 不要预测未来的测试
- 保持测试关注可观察的行为

### 4. 重构

所有测试通过后，寻找[重构机会](refactoring.md)：

- [ ] 提取重复代码
- [ ] 深化模块（把复杂逻辑隐藏在简单接口后面）
- [ ] 在自然的地方应用 SOLID 原则
- [ ] 思考新代码揭示了现有代码的什么问题
- [ ] 每次重构后运行测试

**永远不要在 RED 状态下重构。** 先回到 GREEN。

## 每个周期的检查清单

```
[ ] 测试描述的是行为，而非实现
[ ] 测试只使用公共接口
[ ] 测试能够经受住内部重构
[ ] 代码量刚好满足当前测试
[ ] 没有添加推测性的功能
```

