关于 test-driven-development
面向开发者的测试驱动开发技能,要求先编写失败测试,再按 RED-GREEN-REFACTOR 流程实现。各阶段均设有检查条件,要求修改后运行测试,并限制每步实现范围。
主要功能
- 01实现前先编写一个失败测试
- 02只编写通过测试所需的代码
- 03各阶段运行完整测试套件
- 04在不改变行为的前提下重构
- 05根据待测行为选择测试类型
适用场景
- 01按单个行为逐步实现功能
- 02修复故障时补充回归测试
- 03重构代码并检查行为变化
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ pixel-process-ug/ superkit-agents --skill test-driven-development安装后在 Claude Code 中输入/test-driven-development确认已加载。
附带文件testing-anti-patterns.md
详细介绍
测试驱动开发
概述
TDD 将 RED-GREEN-REFACTOR 循环作为一项不可打破的准则:编写一个失败的测试,用最少的代码让它通过,然后进行清理。这项技能可防止未经测试的生产代码出现,并确保每一行实现代码都由经过验证的需求驱动。
开始时声明:“我正在使用 test-driven-development 技能,并遵循 RED-GREEN-REFACTOR 循环。”
铁律
┌─────────────────────────────────────────────────────────────────┐
│ HARD-GATE: NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST │
│ │
│ This is non-negotiable. There are no exceptions. If you are │
│ writing production code and there is no failing test demanding │
│ that code, you are violating this skill. STOP immediately │
│ and write the test first. │
└─────────────────────────────────────────────────────────────────┘
阶段 1:RED(编写一个失败的测试)
**目标:**只编写一个因正确原因而失败的测试。
操作
- 找出接下来要实现的最小行为单元
- 编写一个断言该行为存在的测试
- 运行测试套件——确认新测试失败
- 阅读失败信息——确认失败原因正确(缺少功能,而不是语法错误或导入错误)
- 如果失败原因不正确,修复测试,直到它以正确的方式失败
停止——硬性门槛:在进入 GREEN 阶段之前,不得继续:
- 已编写并保存测试
- 已运行测试套件
- 新测试失败
- 失败原因正确(测试的是预期行为)
阶段 2:GREEN(让测试通过)
**目标:**编写让失败测试通过所需的最少生产代码。
操作
- 只编写足以让失败测试通过的代码
- 不要重构。不要清理。不要优化
- 如果硬编码值能让测试通过,就可以这样做
- 运行完整的测试套件
- 所有测试都必须通过(不只是新测试)
停止——硬性门槛:在进入 REFACTOR 阶段之前,不得继续:
- 已编写生产代码
- 已运行完整的测试套件
- 所有测试都通过(新测试和现有测试)
- 编写的代码没有超出必要范围
阶段 3:REFACTOR(清理)
**目标:**在不改变行为的前提下提升代码质量。
操作
- 检查重复代码、糟糕的命名、过长的方法和代码异味
- 每次只进行一项重构
- 每次更改后都运行完整的测试套件
- 如果任何测试失败,立即撤销此次重构
- 持续进行,直到代码整洁
停止——硬性门槛:在进入下一个 RED 阶段之前,不得继续:
- 代码整洁且易读
- 重构后所有测试仍然通过
- 重构过程中没有改变行为
硬性门槛的执行
┌─────────────────────────────────────────────────────────────┐
│ HARD-GATE: PHASE COMPLETION CHECK │
│ │
│ Before moving to next phase, ALL items in the │
│ STOP MARKER checklist must be satisfied. │
│ │
│ If ANY item is not satisfied: │
│ → STOP │
│ → Complete the missing item │
│ → Re-verify ALL items │
│ → ONLY THEN proceed │
└─────────────────────────────────────────────────────────────┘
监视模式准则
每次更改任何文件(测试文件或生产代码文件)后,都要运行相关测试套件。没有例外。
| 操作 | 运行测试? | 预期结果 |
|---|---|---|
| 编写测试 | 是 | 失败(RED) |
| 编写生产代码 | 是 | 通过(GREEN) |
| 重构代码 | 是 | 通过(仍为 GREEN) |
| 任何其他编辑 | 是 | 无回归 |
如果测试运行器支持监视模式,请使用。如果不支持,则每次保存后都手动运行测试。
决策表:测试类型选择
| 要测试的行为 | 测试类型 | 框架示例 |
|---|---|---|
| 纯函数逻辑 | 单元测试 | Vitest、pytest、cargo test |
| API 端点请求/响应 | 集成测试 | Supertest、httpx |
| 数据库查询正确性 | 集成测试 | Testcontainers |
| UI 组件渲染 | 单元测试 | React Testing Library |
| 完整用户工作流 | E2E 测试 | Playwright |
| 错误处理路径 | 单元测试 | Vitest、pytest |
示例循环
Requirement: "Users can register with email and password"
Behavior List:
1. Registration with valid email and password succeeds
2. Registration fails if email is empty
3. Registration fails if password is too short
4. Registration fails if email is already taken
Cycle 1 - Behavior 1:
RED: test_registration_with_valid_email_and_password_succeeds → FAIL (no register function)
GREEN: def register(email, password): return User(email=email) → PASS
REFACTOR: rename variable for clarity → PASS
Cycle 2 - Behavior 2:
RED: test_registration_fails_if_email_is_empty → FAIL (no validation)
GREEN: add if not email: raise ValueError → PASS
REFACTOR: extract validation to separate method → PASS
...continue for each behavior...
清单:使用 TDD 开始开发新功能
- 编写任何代码之前,充分理解需求
- 将需求拆分为一系列具体行为
- 按从最简单到最复杂的顺序排列行为
- 为第一个行为创建任务
- 进入 RED 阶段:为第一个行为编写失败的测试
- 进入 GREEN 阶段:编写让测试通过的最少代码
- 进入 REFACTOR 阶段:进行清理
- 为下一个行为创建任务,并从步骤 5 开始重复
- 所有行为实现后,运行完整的测试套件
- 在宣告完成之前,调用
verification-before-completion
测试质量标准
每个测试都必须:
| 标准 | 定义 |
|---|---|
| 快速 | 毫秒级,而非秒级 |
| 隔离 | 测试之间没有共享状态,也不依赖测试顺序 |
| 可重复 | 每次结果都相同,不会时好时坏 |
| 可自验证 | 结果要么通过,要么失败,无需人工判断 |
| 及时 | 在生产代码之前编写(这正是测试驱动开发的要义) |
每个测试都应该:
- 测试一种行为或场景
- 使用能说明场景和预期结果的描述性名称
- 遵循 Arrange-Act-Assert(或 Given-When-Then)结构
- 使用最少的必要准备工作
- 断言结果,而非实现细节
反模式 / 常见错误
| 反模式 | 错误原因 | 正确做法 |
|---|---|---|
| 先写生产代码 | 违背测试驱动开发的目的;测试会被设计成让代码通过 | 始终先写测试 |
| 先写多个测试,再写代码 | 批量测试违背增量式设计原则 | 一次一个测试,一个周期 |
| 测试第一次运行就通过 | 要么测试有问题,要么该行为已经存在 | 继续之前先调查 |
| 在 GREEN 阶段花费超过 5 分钟 | 一次写入的代码过多 | 简化;让测试更具体 |
| 修改测试以匹配代码 | 测试规定行为;代码必须符合测试 | 修复代码,而不是测试 |
| 跳过 REFACTOR 阶段 | 技术债会迅速累积 | 每个周期都进行重构 |
| 每次修改后不运行测试 | 回归问题会被忽略 | 每次保存后都运行测试 |
防止自我合理化
| 借口 | 事实 |
|---|---|
| “这只是一个小改动” | 小改动也会导致生产环境故障。测试它。 |
| “我之后会写测试” | 你不会这么做。即使写了,测试也会更弱,因为它们是为了让代码通过而设计的,而非用于明确规范。 |
| “这只是一次重构” | 重构改变行为的频率比你想象的更高。测试套件可以证明它们不会改变行为。 |
| “我知道这样能用” | 你不知道。你只是以为自己知道。测试才能证明。 |
| “测试会拖慢我” | 没有测试的调试会让你慢 10 倍。 |
| “这段代码太简单,不值得测试” | 如果它简单到不值得测试,那它也简单到不可能出错——所以测试会很容易写。写出来。 |
| “因为有依赖,我无法测试它” | 那说明你的设计存在耦合问题。改进设计。 |
| “写测试比写代码更难” | 这说明你还没有充分理解需求。测试会迫使你澄清需求。 |
| “我只要手动验证一下” | 手动验证无法重复、没有文档记录,也不可信。 |
| “这只是临时代码 / 原型代码” | 原型代码往往会变成生产代码。现在测试,免得以后后悔。 |
| “这个框架让测试变得很难” | 使用框架的测试工具,或将逻辑与框架隔离。 |
| “我时间紧迫” | 在任何超过 20 分钟的时间跨度内,测试驱动开发都更快。正因为有压力,你才更需要它。 |
危险信号
如果发现以下任何情况,请立即停止并重新评估:
| 危险信号 | 意味着什么 | 行动 |
|---|---|---|
| 没有失败的测试就编写生产代码 | 立即违反原则 | 停下。先写测试。 |
| 测试第一次运行就立即通过 | 测试有问题,或该行为已经存在 | 继续之前先调查 |
| 在 GREEN 阶段花费超过 5 分钟 | 编写的代码过多 | 简化。让测试更具体。 |
| 重构改变了行为 | 测试覆盖存在缺口 | 补充缺失的测试 |
| 修改测试以使其通过 | 需求被颠倒了 | 修复代码,使其符合测试 |
| 编写生产代码之前先写多个测试 | 批量测试违背目的 | 一次只写一个测试 |
| 修改后没有运行测试套件 | 回归问题无法被发现 | 运行测试。始终如此。每次都要运行。 |
集成点
| 技能 | 关系 |
|---|---|
verification-before-completion | 在声称任何 TDD 工作已完成之前,必须调用 |
systematic-debugging | REFACTOR 期间测试意外失败时,切换到调试 |
code-review | 通过 TDD 完成功能后,检查测试套件是否完整 |
acceptance-testing | 验收标准决定 TDD 周期的行为列表 |
planning | 规划将功能拆分为适合 TDD 周期的行为 |
testing-strategy | 策略定义框架;TDD 定义周期 |
TDD 中的测试类型
| 类型 | 范围 | 速度 | 编写时机 |
|---|---|---|---|
| 单元测试(主要) | 单个函数、方法、类 | 毫秒 | 每个行为的 RED 阶段 |
| 集成测试(次要) | 组件交互 | 秒 | 单元测试覆盖各个行为之后 |
| 端到端测试(第三) | 完整的用户工作流 | 秒至分钟 | 单元测试和集成测试稳定后,测试关键路径 |
技能类型
严格 — RED-GREEN-REFACTOR 周期是强制性的,不得重新排序、跳过或合并。每个阶段都有必须满足的硬性门槛,才能继续。必须先有失败的测试,才能编写生产代码。
评论
支持 Markdown