Superpowers - 12 没有失败测试,就没有生产代码:从 Superpowers 看“铁律级”测试驱动开发
文章目录
- Pre
- 一、为什么还要再谈 TDD?
- 二、TDD 铁律:比“最佳实践”更高一档的约束
- 三、六步 TDD 循环:比“红-绿-重构”多出来的东西
- 四、用 TDD 修复 Bug:不许“凭感觉修”
- 五、TDD 在完整工作流里的位置:不是孤岛,而是“骨干规范”
- 六、什么是“好测试”?——三指标与典型对比
- 七、五大测试反模式:如何识别 & 如何纠正
- 八、对“合理化借口”的逐一拆解:为什么你以为“在务实”,其实只是在挖坑
- 九、危险信号与故障排除:什么时候要“停下来删掉重来”
- 十、TDD 验证清单:可以当“出厂质检表”用
- 十一、如何把这套“铁律级 TDD”引入自己的团队?
- 结语:TDD 不只是“怎么写代码”,而是“怎么证明代码该存在”

Pre
Superpowers - 01 让 AI 真正“懂工程”:Superpowers 软件开发工作流深度解析
Superpowers - 02 用 15 个技能给你的 AI 装上「工程大脑」:Superpowers 快速开始深度解析
Superpowers - 03 一文搞懂 Superpowers:面向多平台 AI 编码助手的安装与实践指南
Superpowers - 04 从“会写代码”到“会做工程”:Superpowers 工作流引擎架构深度剖析
Superpowers - 05 构建一个“会自己找插件用”的 Agent:深入解析 Superpowers 的技能发现与激活机制
Superpowers - 06 从文档到“结构契约”:Superpowers 技能剖析与 Frontmatter 深度解读
Superpowers - 07 从 SessionStart Hook 看 Superpowers:把「技能库」变成「行为操作系统」
Superpowers - 08 在 AI 时代重写「需求评审会」:深入解读 Superpowers 的头脑风暴与设计规范机制
Superpowers - 09 从构思到落地:如何用「计划编写与任务粒度」驾驭 AI 时代的软件开发
Superpowers - 10 用 Subagent 驱动开发,把「AI 写代码」变成一条严谨的生产流水线
Superpowers - 11 从计划到落地:深入解析 Superpowers 的「内联执行计划」工作流
读者对象:有一定工程经验的开发者、对工程实践和 AI 辅助开发感兴趣的研究者与技术爱好者,希望把 TDD 从“知道”升级到“做到、做稳”的人。
一、为什么还要再谈 TDD?
TDD(Test-Driven Development)早已不是新名词:先写测试、再写代码、最后重构——“红-绿-重构”这套流程,多数人都能背出来。
但在真实团队里,TDD 常常演变成另一幅景象:
- 赶进度时:“这次先写代码,测试之后补”
- 修 bug 时:“我已经手动测过边界情况了”
- 遇到遗留系统:“这块太难写测试了,只能先改了再说”
Superpowers 这套工作流体系,在它的《测试驱动开发循环》文档中,把 TDD 从“工程建议”升级为零妥协的质量准则:
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST —— 没有先失败的测试,就没有生产代码。
这不是口号,而是一条带有配套流程、反模式清单、验证清单和跨技能集成点的“铁律”。
本文就沿着这份文档,系统拆解这条铁律背后的设计:为什么要这么严、具体怎么做、常见坑有哪些、如何在现代工作流(尤其是 AI 辅助开发场景)中落地。
二、TDD 铁律:比“最佳实践”更高一档的约束
2.1 规则本身:写错了可以改,顺序错了要删
Superpowers 给 TDD 下的第一条规则非常直接:
- 任何生产代码,都必须有对应测试,而且这个测试在实现之前曾经失败过。
- 如果你先写了实现,再补测试:
- 必须把那段实现 彻底删除:
- 不能当“参考实现”留在某处;
- 写测试时不能“偷看”它;
- 重写实现时也不能把它当模板。
- 必须把那段实现 彻底删除:
原因也写得很白:这是为了对抗认知偏差——一旦你先写了实现,大脑会自然倾向于写“能证明这段实现没错”的测试,而不是验证“这段行为应该是什么”。
事后补的测试回答的是:“它现在在做什么?”
先写的测试回答的是:“它应该做什么?”
唯一被允许的例外,是一次性原型、生成代码和配置文件,而且必须经过“人类搭档”的明确许可。这也暗示了一个前提:这套工作流默认你在一个“人 + AI”协作环境里工作,AI 不能自说自话给自己开“例外许可”。
2.2 为什么是“删掉”,而不是“加个标签留着”
为什么要“删到看不见”:任何被保留下来的未验证实现,都会在心理上形成“引力场”。稍不注意,你就会开始改写它,而不是从需求推导出一个更干净的解。
这条规则的实际含义是:
- 违反 TDD 的成本被显式抬高:
- 不是“多写点测试就算补救”;
- 而是“你刚才那一小时的工作要全部作废”。
- 于是你会更谨慎地在一开始就说服自己:“这次就先写实现吧”——因为代价不是“挨一顿 code review”,而是“删掉重来”。
三、六步 TDD 循环:比“红-绿-重构”多出来的东西
书上讲 TDD 常常说三步:Red → Green → Refactor。
Superpowers 把它拆成了六步、带强制验证关卡的闭环:
- RED:写一个会失败的测试
- 验证 RED:运行测试,确认 因正确原因失败
- GREEN:写最小实现让测试通过
- 验证 GREEN:运行测试,确认 所有测试全绿且输出干净
- REFACTOR:在保持全绿的前提下重构
- 下一轮测试:回到 RED,继续下一个行为
3.1 RED —— 测试必须针对“真实行为”
在 RED 步骤,文档强调几点:
- 每个测试只覆盖 一个行为(名称中如果有“和”,就是危险信号);
- 尽量使用 真实组件和真实代码路径,只在不得不隔离副作用或外部依赖时才使用 mock;
- 测试的名字和结构,要能清晰展示 期望的 API 契约。
一个典型例子:重试逻辑的两种写法。
| 写法 | 示例思路 |
|---|---|
| 测真实行为 | 传入一个会失败几次再成功的操作,用变量记录执行次数,断言“最多执行 3 次且最终成功” |
| 测 mock 行为 | mock 掉操作函数,断言“mock 被调用了 3 次” |
Superpowers 明确站队前者:测试的对象是行为,不是 mock 本身。
3.2 验证 RED —— 失败也要“失败得对”
这一关卡常常被很多人跳过:写了测试就直接去写实现。Superpowers 强制要求,你必须先:
- 运行测试,并观察结果;
- 要求结果满足:
- 是一个“失败”,而不是“报错崩溃”;
- 失败的消息和数据,符合你预期的缺失行为。
如果:
- 测试一上来就通过:说明你测试的是已有行为,不是你要引入的新能力——需要重新设计测试;
- 测试因为意料之外的异常抛错:说明测试本身有 bug,而不是系统暂时缺少的行为。你需要先修正测试,再继续循环。
这一步的价值在于:确认测试本身是“有效告警器”,否则后面所有的 “绿” 都失去意义。
3.3 GREEN —— 最小可行实现(强制 YAGNI)
实现阶段的指导原则是:用最少的代码让测试通过。 文档特地列了一个重试逻辑的对比:
- 好的版本:几行循环,固定次数尝试,失败就抛;
- 坏的版本:一上来就支持退避策略、扩展配置对象、生命周期回调……这些当前测试根本不需要的复杂度。
这里把 YAGNI(You Aren’t Gonna Need It)从理念落到了规则层面:
- 任何测试不要求的行为和配置,先不要写;
- 真需要时,让新测试来“逼”出这些能力。
3.4 验证 GREEN —— 不只是“这一个测试过了”
验证 GREEN 阶段除了确认刚写的测试通过,还有两个关键要求:
- 所有既有测试也必须继续通过:避免引入回归;
- 测试输出必须干净:不能夹带错误和警告。
这比“让 CI pipeline 过一遍”更近一步:
每次循环都在本地主动确认“全绿 + 无噪音”,把“有警告但先不管”的习惯扼杀在萌芽状态。
3.5 REFACTOR —— 清理代码,而不是插新功能
重构阶段的范围被限制在三件事:
- 消除重复;
- 改善命名;
- 提取辅助函数。
显式禁止在这一步引入新行为。如果你发现需要新行为,就回到 RED 写新测试。
整个循环因此成了一个可验证的小步快跑机制:
每一次行为改动都伴随“失败的证据” → “通过的证据” → “设计清理”三个阶段。
四、用 TDD 修复 Bug:不许“凭感觉修”
TDD 不只适用于新功能,同样适用于 bug 修复。
举一个典型例子:“空邮箱也被接受”的 bug。
流程被明确写成一个 TDD 循环:
- RED:写
test('rejects empty email'),期望返回错误"Email required"; - 验证 RED:测试失败,提示
expected 'Email required', got undefined——正是我们要修的行为; - GREEN:在实现里加入
if (!data.email?.trim())这样的 guard 分支; - 验证 GREEN:测试通过;
- REFACTOR:如果还有其他字段的类似校验,可以抽取公共验证逻辑。
配套的“系统化调试流程”技能还强调:在任何修复之前,必须先做根因分析;TDD 则负责把修复结果“锁死”在测试里,防止回归。
核心观点是:
没有能复现 bug 并验证修复的测试,就不算真正修复。
五、TDD 在完整工作流里的位置:不是孤岛,而是“骨干规范”
Superpowers 把 TDD 嵌入到了整条开发流水线中,几个关键集成点如下。
5.1 与 Subagent 驱动开发:AI 也必须守 TDD
在 Subagent 驱动开发中,Superpowers 会把某些实现任务交给新的“subagent”(可以理解为子 AI Agent)。每个 subagent 在接收到任务时,都会一并收到 TDD 要求。
审查流程分两层:
- 规范合规:看它是否按设计规范实现;
- 代码质量审查:其中之一就是检查是否按红-绿-重构循环执行、是否存在非 TDD 的“裸实现”。
这意味着:即使是 AI 写的代码,也不能跳过“先写失败测试”的环节,否则会在审查阶段被驳回。
5.2 与“内联执行计划”和“完成前验证”:每一步都要有证据
在“内联执行计划”技能中,每个任务的执行都需要伴随验证动作,TDD 循环提供了验证的工具。
而“完成前验证”技能,则把 TDD 思维推进得更极致:
对于回归测试,需要完整跑一遍序列:
- 写回归测试;
- 在当前状态下运行,测试应当通过(因为当前版本没有 bug);
- 撤销修复;
- 再运行,测试必须失败;
- 恢复修复,再运行,确认通过。
这套流程在实践中非常少见,但从逻辑上极其严谨:它证明你的测试既能抓住 bug,也不会在没有 bug 时“误报”。
5.3 与调试和 Code Review 的联动
TDD 的原则也被灌注到系统化调试流程和代码审查工作流中:
- 调试流程要求:每一个修复应当有“失败测试 → 修复 → 通过”的证据链;
- Code Review 指引:不仅看实现是不是“优雅”,还会检查
- 是否有对应测试;
- 测试是否在实现之前存在并失败过;
- 是否覆写了真实行为而非 mock 行为。
TDD 在这里已经不再是“团队文化共识”,而是一套可检查、可拒绝提交的规则。
六、什么是“好测试”?——三指标与典型对比
“好测试” 三个特征:最小化、清晰、彰显意图。
| 质量维度 | 好的示例 | 坏的示例 |
|---|---|---|
| 最小化 | 每个测试只验证一种行为 | test('validates email and domain and whitespace') 同时测多个东西 |
| 清晰 | test('retries failed operations 3 times') |
test('test1') 这种没有任何语义的名字 |
| 彰显意图 | 测试明确展示期望的 API 契约 | 隐藏真实行为,让人看不出“代码应该做什么” |
这三个指标的一个共同出发点是:测试本身是一种文档。
好的测试应该让你不看实现就能知道“这个系统对外承诺了什么行为”。
七、五大测试反模式:如何识别 & 如何纠正
配套的 testing-anti-patterns.md 文档列出了五类常见的测试反模式,每一类都配有示例、危害分析、改写版本以及“关卡问题”(用于在写测试前自查)。
7.1 反模式总览
| 反模式 | 核心问题 | 关卡函数问题(写之前问自己) |
|---|---|---|
| 测试 mock 行为 | 断言的是 mock 是否被调用,而非真实行为 | “我是在测试真实组件行为,还是只在测试 mock 的存在?” |
| 生产代码中的测试专属方法 | 为了测试在生产类里加只给测试用的方法 | “这个方法是否只被测试使用?” → 是就挪到测试工具类 |
| 盲目 mock | 随手把依赖都 mock 掉,忽略真实副作用 | “真实方法有哪些副作用?这些副作用能否被更好地测试?” |
| 不完整的 mock | 只 mock 部分字段,导致与真实返回结构不一致 | “真实 API 响应包含哪些字段?我是不是在制造一个假世界?” |
| 事后补测试 | 把测试当成实现完成后的装饰步骤 | 用 TDD 循环替代:“测试→实现→重构→声明完成” |
这份文档的核心原则是:
测试应该验证真实行为,而不是 mock 行为。
Mock 是隔离的工具,不是被验证的目标。
同时还有一条非常实用的经验规则:
如果 mock 配置代码占了测试的一半以上,这是危险信号,应考虑写更偏集成层的测试,直接对真实组件下手。
7.2 严格 TDD 如何天然免疫这些反模式
一个有趣的观察:只要你真的严格执行“先写失败测试、再写实现”,你很难落入这些反模式。
原因在于:
- 在 RED 阶段,你还没有实现,也没有 mock 细节,自然更倾向于以“行为”来描述测试;
- 如果一开始就走向“测试 mock 行为”,往往意味着你的出发点已经是“如何验证这段实现”,而不是“需求是什么”。
简单说:守住 TDD 顺序,本身就是对测试质量的强约束。
八、对“合理化借口”的逐一拆解:为什么你以为“在务实”,其实只是在挖坑
几类开发者常见的“反 TDD 借口”,并给出对应的“现实版本”。
| 合理化说法 | 现实情况 |
|---|---|
| “事后补测试效果一样” | 补测试只能回答“现在做了什么”,无法约束“应该做什么” |
| “我已经手动测了所有边界情况” | 手动测试既不可重复,也不会留痕;“我试的时候能过”不代表覆盖充分 |
| “删掉 X 小时工作太浪费了” | 这是典型沉没成本谬误;留下未经验证代码才是真正的技术债务 |
| “TDD 太教条了,务实一点更重要” | 所谓“务实”的捷径,大多变成“在线上环境调试”——既慢又危险 |
| “我要先随便写写探索一下” | 探索没问题,但探索代码用完就丢;真正要保留的实现必须从 TDD 开始 |
一个特别值得注意的观点是:
测试优先与事后测试不是对称关系。
- 实现后写测试:思路被现有实现结构绑定,很难跳出既有分支去想“还有什么边界没覆盖”;
- 实现前写测试:你被迫围绕需求和边界来设计行为,从而自然更容易想到“如果为空?如果超长?如果重复?”这类情况。
九、危险信号与故障排除:什么时候要“停下来删掉重来”
几类表明 TDD 纪律已经被破坏的危险信号:
- 先写了实现才想到测试;
- 测试一写就通过,从未经历过“红”;
- 给自己找形形色色的“这一次不 TDD 的理由”;
- 以“参考实现”的名义保存未经测试的代码。
统一的处理方案只有一个:删掉相关实现,从写测试开始重来。
同时,文档也给了一份“测试难度 = 设计问题”的故障排除表,用来应对下面这些常见阻力:
| 遇到的问题 | 建议的应对方案 |
|---|---|
| 不知道如何写测试 | 先写出期望的 API 与断言,必要时和人类搭档一起讨论 |
| 测试写起来太复杂 | 设计太复杂;考虑收敛接口或拆分职责 |
| 感觉处处要 mock | 耦合度太高;尝试依赖注入,降低组件之间的硬耦合 |
| 测试 setup 特别庞大 | 抽取公共辅助函数;仍然庞大则往上看:设计是否过于臃肿? |
这套观念的重点是:
“难测试”不是测试的问题,是设计问题的诊断信号。
十、TDD 验证清单:可以当“出厂质检表”用
在把任何工作标记为完成之前,Superpowers 要求至少满足以下八条检查项:
- 每个新函数或方法都有测试;
- 每个测试在实现之前都被观察到失败;
- 每个测试是因为预期的行为缺失而失败,而不是因为打字错误之类的意外;
- 为让每个测试通过,你都只写了最小实现;
- 所有测试当前都处于通过状态;
- 测试输出干净,没有错误和警告;
- 测试用的是尽可能真实的代码,仅在必须时使用 mock;
- 重要的边界情况和错误路径都被覆盖。
最后把所有内容再次压缩为一句话:
任何生产代码,如果没有一个曾经失败、现在通过的测试做背书,就不算 TDD。
除非你的人类搭档明确批准,否则不得例外。
十一、如何把这套“铁律级 TDD”引入自己的团队?
结合上面的内容,可以给出一套相对可落地的引入路径:
-
从“验证 RED / 验证 GREEN”开始
哪怕暂时做不到所有代码都先写测试,也可以先在现有流程中加两条习惯:- 每次写完测试先确认它会因为缺少行为而失败;
- 每次改完实现跑一遍所有测试,确保全绿、无 warning。
-
把“删掉非 TDD 实现”变成约定俗成
- 在团队规范里写明:如果发现自己“先写了实现”,就自觉删掉那段实现,再从测试开始;
- code review 时可以温和但坚定地提醒这条规则。
-
对 bug 修复强制使用 TDD
- 所有 bug 修复都必须包含一个能复现 bug 的测试;
- 没有测试的修复不允许合并(可以先在人少的模块上试行)。
-
逐步引入反模式检查
- 在 review 中有意识地检查:这个测试是不是在测 mock 行为?有没有“测试专属的生产方法”?
- 对特别依赖 mock 的测试,尝试改写为更整合的行为测试。
-
在 AI 协作场景里明确要求“子 agent 也要 TDD”
- 如果你在使用类似 Superpowers 的工具或其他 AI 编程助手,可以在提示词中明确:
- “先写失败测试,再写实现”;
- “所有新代码都需要有测试,并展示测试从失败到通过的过程”。
- 如果你在使用类似 Superpowers 的工具或其他 AI 编程助手,可以在提示词中明确:
结语:TDD 不只是“怎么写代码”,而是“怎么证明代码该存在”
Superpowers 的这份《测试驱动开发循环》文档,真正推到极致的其实是一件事:
代码存在的前提,是有证据证明它在做它应该做的事。
- TDD 铁律规定了证据必须以“先失败再通过的测试”这种形式出现;
- 六步循环保证了你随时都能指出“这段行为是在哪一轮迭代中被引入的”;
- 反模式与验证清单,则帮助你把 TDD 从“口头信念”变成“可检查的工程纪律”。
在一个越来越依赖自动生成代码、快速演进系统的世界,这套看似“古典”的实践反而变得愈发重要:
当实现的生产越来越轻松时,我们更需要严肃地对待“为什么这段实现应该被保留下来”。
而在这条路上,“没有失败测试,就没有生产代码”,也许是最简单粗暴、但也最有效的一条起跑线。

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)