# Tdd

> 使用 RED-GREEN-重构（red-green-refactor）循环进行测试驱动开发。适用于用户希望用 TDD 构建新功能或修复 bug，提到 “red-green-refactor”，希望使用集成测试，或询问“test-first development（先写测试）”。

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

---


# 测试驱动开发（TDD）

## 哲学（Philosophy）

**核心原则**：测试应当通过公共接口验证行为，而不是依赖实现细节。代码可以被完全重写；测试不应该随之频繁改变。

**好的测试**具有集成式特征：它会通过公共 API 实际跑通真实代码路径。它描述系统“做什么”，而不是“怎么做”。好的测试读起来像规范——例如“用户在使用有效购物车时可以结账”，你就能明确知道系统具备这个能力。由于测试不关心内部结构，它能在重构后继续存活。

**坏的测试**会耦合到实现。它们 mock 内部协作者、测试私有方法，或用“绕过接口”的方式验证（例如直接查询数据库而不是使用接口）。危险信号是：当你重构但行为没有变化时，测试却失败了；如果你重命名了某个内部函数导致测试失败，但用户可观察行为却没有变，这些测试是在测实现而不是在测行为。

查看 [tests.md](tests.md) 获取更多示例，并查看 [mocking.md](mocking.md) 了解 mock 指南。

## 反模式：横向切片（Horizontal Slices）

**不要写“先写完所有测试，再写所有实现”。**

这是一种“把 RED 当成写所有测试”的横向切片：把 RED 理解成“先写完所有测试”，把 GREEN 理解成“一次性写完所有代码”。

这会产生 **烂测试（crap tests）**，包括：

- 大量批量编写的测试验证的是“想象出来的行为”，而不是“真实的行为”
- 测试会变成在测“结构形状”（数据结构、函数签名），而不是用户可见的行为
- 测试会对真实变化变得不敏感：行为坏了反而测试仍可能通过；行为没坏时反而会失败
- 你会在还没理解实现之前就“把车灯开出来”，过早承诺测试结构

**正确做法**：通过 tracer bullets 进行竖向切片（vertical slices）。一个测试 → 一个实现 → 重复。每一轮测试都会基于上一轮你从代码/理解中学到的东西来推进。由于你刚刚写完代码，所以你会非常清楚“哪些行为重要”以及“用什么方式验证它”。

```
WRONG（横向）：
  RED:   test1, test2, test3, test4, test5
  GREEN: impl1, impl2, impl3, impl4, impl5

RIGHT（竖向）：
  RED→GREEN: test1→impl1
  RED→GREEN: test2→impl2
  RED→GREEN: test3→impl3
  ...
```

## 工作流（Workflow）

### 1. 规划（Planning）

在写任何代码之前：

- [ ] 与用户确认需要如何修改接口（interface changes）
- [ ] 与用户确认需要测试哪些行为（优先级更高）
- [ ] 识别可以提取的 [深模块（deep modules）](deep-modules.md)（小接口、深实现）
- [ ] 为 [可测试性（testability）](interface-design.md) 设计接口
- [ ] 列出要测试的行为（而不是实现步骤）
- [ ] 让用户批准该计划

提问：`公共接口应当长什么样？哪些行为最重要、最需要先测？`

**你无法把所有东西都测完。** 请与用户明确“哪些行为最关键”。把测试资源集中在关键路径与复杂逻辑上，而不是覆盖所有可能的边界情况。

### 2. tracer bullet（示踪子弹）

写出 **一个**测试，用来确认系统的 **一个**要点：

```
RED:   为第一个行为写测试 → 测试失败
GREEN: 写最小代码让测试通过 → 测试通过
```

这就是你的 tracer bullet——它证明这条路径能端到端跑通。

### 3. 逐步循环（Incremental Loop）

对剩余的每个行为：

```
RED:   写下一个测试 → 失败
GREEN: 最小实现让它通过 → 通过
```

规则：

- 一次一个测试
- 只写足够通过当前测试的代码
- 不要提前为未来测试“预埋”
- 保持测试聚焦在可观察的行为

### 4. 重构（Refactor）

当所有测试都通过之后，再查看 [重构候选（refactoring.md）](refactoring.md)：

- [ ] 抽取重复逻辑
- [ ] 加深模块（把复杂度移到简单接口背后）
- [ ] 在自然的地方应用 SOLID 原则
- [ ] 评估新代码暴露出的、对现有代码的潜在问题
- [ ] 每完成一个重构步骤就运行测试

**不要在 RED 状态下重构。** 先到 GREEN 再谈重构。

## 每一轮循环的清单（Checklist Per Cycle）

```
[ ] 测试描述的是行为，而不是实现
[ ] 测试只使用公共接口
[ ] 测试在内部重构后仍能存活
[ ] 针对该测试的代码足够最小
[ ] 没有加入“为了以后而猜”的功能
```


