| name | tdd |
| description | 当用户要求 TDD、测试驱动开发、先写失败测试、红绿循环或测试先行,或在实现新功能/修复 bug 时
明确要求先测试后编码时使用。
|
| metadata | {"openclaw":{"emoji":"🧱"}} |
tdd — 测试驱动开发技能
执行前置
遵循当前目录 AGENTS.md「技能执行公共契约」;仅按需读取技能正文与 reference。
先写失败测试 → 看它失败 → 写最小代码 → 看它通过 → 重构保持全绿,逐功能循环。
核心原则:没看到测试失败,就无法知道测试是否测对了东西。
核心原则
- 铁律(Iron Law):
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST——
先写代码再写测试?删除重来。无例外:不留作"参考"、不"边写测试边适配"、
不看它;删除就是删除。
- RED-GREEN-REFACTOR:RED 写一个最小失败测试 → 验证它确实因特性缺失而
失败(不是报错、不是拼写错误)→ GREEN 写恰好够用的代码 → 验证通过且
其他测试仍绿 → REFACTOR 只在绿后清理(去重/改名/提取),保持全绿不添行为。
- 没看失败=不知道测对没有:测试通过可能是测了已存在的行为或测了实现细节;
只有先看到它失败(缺特性),才能证明它能抓住这个 bug。
- 测试质量三要素:最小(一个行为,名字含"and"就拆分)、清晰(名字描述
行为而非 test1)、表意(断言真实行为;模拟对象除非无法避免)。
- 反合理化:以"太简单/先测后测一样/手动测过了"等借口跳过 TDD 即违规;
逐条对照反合理化表与红旗清单,出现即停下从头开始。
- 修 bug 也是 TDD:发现 bug 先写复现它的失败测试,再走红绿循环——测试既
证明修复又防回归;从不无测试修 bug。
- 豁免需用户批准:一次性原型、生成代码、配置文件可问用户是否豁免 TDD,
其余场景无例外。
触发时机
- 用户要求测试驱动开发:"tdd"、"测试驱动"、"先写测试再实现"、"红绿循环"、
"先写失败用例"、"测试先行"
- 用户要求修 bug 时补测试:"修这个bug时先写个测试"、"给这个bug写回归测试"
- 新功能/修复实现中(用户未明确要求但场景符合)——按"何时使用"判定
- 与其他技能配合:实现计划编写用 plan(其任务粒度即红绿循环);
验证既有产出用 test;审查代码用 review;实现报错用 debug;
需求澄清用 brainstorm
工作流程
Step 1. 明确目标与测试框架
- 确定功能/bug 的最小行为描述(一个行为一个循环);
- 确认测试框架与命令(如
pytest、npm test <file>),在终端摘要中说明;
- 例外场景(原型/生成代码/配置)先问用户是否豁免。
Step 2. RED — 写失败测试
- 写一个最小测试,命名清晰描述行为(
test('重试失败操作3次'));
- 测试真实代码与真实行为,无必要不用 mock;
- 用"期望 API"写测试(先想调用方想怎么用)。
Step 3. 验证 RED — 看它失败(强制,从不跳过)
<测试命令> 路径/测试文件
确认:测试失败(非报错)、失败信息符合预期、失败原因 = 特性缺失(非拼写/环境);
- 测试直接通过?测的是已有行为 → 修测试;
- 测试报错?修错误直到正确失败。
Step 4. GREEN — 最小实现
- 写恰好能通过测试的代码;不添加测试外的功能、不重构、"不改进";
- 反例即 YAGNI:不要在最小实现里预先加参数/选项。
Step 5. 验证 GREEN — 看它通过(强制)
<测试命令> 路径/测试文件
确认:测试通过、其他测试仍通过、输出干净(无错误/警告);
Step 6. REFACTOR — 清理(仅绿后)
- 去重、改善命名、提取辅助;保持测试全绿;不添加行为;
- 回到 Step 2 下一个功能单元,循环直至功能完成。
Step 7. 完成核对与总结
逐项核对:每函数有测试 / 看过每个测试失败 / 失败原因符合预期 /
写了最小实现 / 全部通过 / 输出干净 / 边界与错误已覆盖。
缺任何一项 = 跳过了 TDD,从头开始。
✓ tdd 完成
目标: <功能/bug 描述>
循环: <N 个红绿循环>
RED: <每个循环的失败测试 + 预期失败原因>
GREEN: <最小实现要点(文件:行号)>
REFACTOR: <清理项,无则省略>
验证: <全绿 + 其他测试无回归 + 输出干净>
豁免: <用户批准的豁免项,无则省略>
遗留: <未覆盖项,无则省略>
反合理化表(跳过 TDD 的借口 → 现实)
| 借口 | 现实 |
|---|
| "太简单不用测" | 简单代码也会坏;测试只需 30 秒 |
| "之后再测" | 后写测试立即通过——什么也证明不了,可能测错东西/测实现/漏掉边角 |
| "先测后测目标一样(精神不是仪式)" | 后测回答"这代码做了什么";先测回答"这代码该做什么" |
| "手动测过了" | 手动测试无记录、不可复现、压力下易漏;自动化每次跑法一致 |
| "删掉 X 小时的工作太浪费" | 沉没成本谬误;保留无法信任的代码才是浪费 |
| "留作参考,之后先写测试" | 你会去适配它——那就是后测。删除就是删除 |
| "先探索一下" | 可以;探索产物扔掉,用 TDD 从头开始 |
| "TDD 拖慢进度" | TDD 才是务实路径:提交前抓 bug、防回归、可无惧重构 |
| "已有代码没有测试" | 那正是你改进它的机会,给既有代码补测试 |
红旗清单(出现即停下重来)
- 代码先于测试;实现后补测试;测试立即通过;
- 无法解释测试为什么失败;"之后加测试";合理化"就这一次";
- "我已手动测过";"精神上是一回事";"留作参考/适配现有代码";
- "已花 X 小时,删了浪费";"TDD 是教条,我在务实";"这不一样因为……"
以上任一出现 = 删除代码,用 TDD 从头开始。
错误处理
| 场景 | 处理 |
|---|
| 不知道怎么写测试 | 写期望 API;先写断言;问用户 |
| 测试太复杂 | 设计太复杂——简化接口 |
| 什么都要 mock | 代码耦合过重——依赖注入 |
| 测试基建庞大 | 提取辅助;仍复杂则简化设计 |
| 测试通过但没看过它失败 | 回退实现,重走 RED→验证失败→GREEN |
| 修 bug 无测试 | 先写复现失败测试,再红绿循环 |
| 用户催促跳过测试 | 说明铁律与理由,申请明确豁免才可跳过 |
| 无测试框架 | 纯 shell 项目用 bash -n + 最小复现脚本走红绿(先写脚本断言) |
注意事项
- 铁律是精神内核:违反字面就是违反精神,无例外条款;
- 每个测试先看过失败再写实现——没看过失败就等于没测对;
- 不越界访问工作目录之外的内容;
- 不删除文件(用户未明确要求时);本次改动按公共 Git 契约检查,不自动暂存、提交或推送;