Superpowers - 12 没有失败测试,就没有生产代码:从 Superpowers 看“铁律级”测试驱动开发

张开发
2026/4/17 20:54:29 15 分钟阅读

分享文章

Superpowers - 12 没有失败测试,就没有生产代码:从 Superpowers 看“铁律级”测试驱动开发
文章目录Pre一、为什么还要再谈 TDD二、TDD 铁律比“最佳实践”更高一档的约束2.1 规则本身写错了可以改**顺序错了要删**2.2 为什么是“删掉”而不是“加个标签留着”三、六步 TDD 循环比“红-绿-重构”多出来的东西3.1 RED —— 测试必须针对“真实行为”3.2 验证 RED —— 失败也要“失败得对”3.3 GREEN —— 最小可行实现强制 YAGNI3.4 验证 GREEN —— 不只是“这一个测试过了”3.5 REFACTOR —— 清理代码而不是插新功能四、用 TDD 修复 Bug不许“凭感觉修”五、TDD 在完整工作流里的位置不是孤岛而是“骨干规范”5.1 与 Subagent 驱动开发AI 也必须守 TDD5.2 与“内联执行计划”和“完成前验证”每一步都要有证据5.3 与调试和 Code Review 的联动六、什么是“好测试”——三指标与典型对比七、五大测试反模式如何识别 如何纠正7.1 反模式总览7.2 严格 TDD 如何天然免疫这些反模式八、对“合理化借口”的逐一拆解为什么你以为“在务实”其实只是在挖坑九、危险信号与故障排除什么时候要“停下来删掉重来”十、TDD 验证清单可以当“出厂质检表”用十一、如何把这套“铁律级 TDD”引入自己的团队结语TDD 不只是“怎么写代码”而是“怎么证明代码该存在”PreSuperpowers - 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 从“知道”升级到“做到、做稳”的人。一、为什么还要再谈 TDDTDDTest-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实现阶段的指导原则是用最少的代码让测试通过。 文档特地列了一个重试逻辑的对比好的版本几行循环固定次数尝试失败就抛坏的版本一上来就支持退避策略、扩展配置对象、生命周期回调……这些当前测试根本不需要的复杂度。这里把 YAGNIYou 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 编程助手可以在提示词中明确“先写失败测试再写实现”“所有新代码都需要有测试并展示测试从失败到通过的过程”。结语TDD 不只是“怎么写代码”而是“怎么证明代码该存在”Superpowers 的这份《测试驱动开发循环》文档真正推到极致的其实是一件事代码存在的前提是有证据证明它在做它应该做的事。TDD 铁律规定了证据必须以“先失败再通过的测试”这种形式出现六步循环保证了你随时都能指出“这段行为是在哪一轮迭代中被引入的”反模式与验证清单则帮助你把 TDD 从“口头信念”变成“可检查的工程纪律”。在一个越来越依赖自动生成代码、快速演进系统的世界这套看似“古典”的实践反而变得愈发重要当实现的生产越来越轻松时我们更需要严肃地对待“为什么这段实现应该被保留下来”。而在这条路上“没有失败测试就没有生产代码”也许是最简单粗暴、但也最有效的一条起跑线。

更多文章