claude-skills 的 TDD 铁律:用 test-master 落实"先写失败测试,再写生产代码"的开发纪律
【免费下载链接】claude-skills67 Specialized Skills for Full-Stack Developers. Transform Claude Code into your expert pair programmer.项目地址: https://gitcode.com/GitHub_Trending/claud/claude-skills
导读
本文深入解读 claude-skills 开源仓库中 test-master 技能所承载的 TDD 铁律(Iron Laws),它源自 obra/superpowers 并经 research/superpowers_research_findings.md 记录为已整合议题(Issue #56)。这套铁律以三条不可妥协的规则约束开发者的测试纪律,要求任何生产代码都必须由一条先被观察到失败的测试来驱动。读完本文,你将掌握三大铁律的完整内涵、RED-GREEN-REFACTOR 循环的落地步骤、常见合理化借口的拒斥方法,以及如何在 claude-skills 的 test-master 工作流(skills/test-master/SKILL.md)中将这套纪律应用到日常开发与 Agent 协作中。
一、核心原则:没有失败的测试,就没有生产代码
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST.(没有先失败的测试,就没有生产代码。)
这是 TDD 铁律文件(skills/test-master/references/tdd-iron-laws.md)开篇就宣告的、不可协商的底线:如果你在写失败测试之前就已经写出了生产代码,那么请删除它、从头开始,没有任何例外。
这一原则在 claude-skills 项目中并非孤立存在,而是被提升到了全局行为约束的层面。在项目根目录的 MODELCLAUDE.md 第 103~112 行的 "Testing Mandate" 一节中,同样的语句被再次强调,并直接与 RED-GREEN-REFACTOR 循环绑定。这意味着,无论使用哪个技能、在哪个工作流阶段,这套纪律都适用于 Claude Code 在该仓库内的所有行为——它不是某个测试文件的建议,而是整个项目 Agent 行为契约的一部分。
从测试反模式的角度看,这一原则也呼应了 skills/test-master/references/testing-anti-patterns.md 中的第五种反模式"集成测试沦为事后补丁"(Integration Tests as Afterthought):把测试当成可选项、先写完功能再补测试,正是 TDD 铁律要根除的最常见实践。
二、三大铁律详解
Iron Law 1:根本法则(The Fundamental Rule)
"You shall not write any production code unless it is to make a failing test pass." (除非是为了让一个失败的测试通过,否则你不得编写任何生产代码。)
每一行生产代码都必须对应一个符合以下三条属性的测试:
- 先被写出(Was written first)
- 被观察到失败(Was observed to fail)
- 因为这段代码而通过(Now passes because of that code)
三者缺一不可。"先被写出"保证了测试不是对既有实现的追认;"被观察到失败"保证了测试确实探测到了缺失的行为;"因为这段代码而通过"保证了生产代码与测试之间存在真实的因果联系。这三个属性共同构成了一条生产代码存在的合法性证明。
Iron Law 2:以观察为证(Proof Through Observation)
"If you didn't watch the test fail, you don't know if it tests the right thing." (如果你没有亲眼看到测试失败,你就不知道它测的是不是正确的东西。)
这条铁律强制要求一个不可省略的验证步骤序列:
- 写下测试
- 运行它,并观察失败
- 确认失败信息是有意义的(而不是因为拼写错误、导入失败等偶然原因而"失败")
- 只有完成上述步骤之后,才去实现修复
原文档中有一句非常尖锐的总结:"一个你从未见过它失败的测试,什么也证明不了。"这正是 Iron Law 1 中"被观察到失败"这一属性的行为化表达——观察失败不是走形式,而是证明测试与行为之间存在真实对应的唯一方式。
Iron Law 3:最终裁决(The Final Rule)
"Production code exists → A test exists that failed first. Otherwise → It's not TDD." (生产代码存在 → 存在一个先失败的测试。否则 → 那就不是 TDD。)
这条铁律划定了 TDD 与非 TDD 之间不存在中间地带:如果代码在编写时没有前置的失败测试,那么无论事后补了多少测试、覆盖率多高,它都不是测试驱动开发。这一点在 research/superpowers.md 的第 3 节"Testing Iron Laws"中有更完整的阐述:Iron Law 3 的唯一例外是获得人类合作者的明确批准,除此之外没有任何豁免——"违反规则的文字,就是违反规则的精神"(Spirit and Letter Alignment),对规则的严格执行与对原则的真正认同同等重要。
三、RED-GREEN-REFACTOR 循环
三大铁律通过 RED-GREEN-REFACTOR 三阶段循环在操作层面落地。原文档(skills/test-master/references/tdd-iron-laws.md)给出了一个从空数组求和的朴素示例,我们在此基础上逐阶段展开。
RED:先写一个最小失败的测试
// Start with the smallest possible failing test it('should return 0 for empty array', () => { expect(sum([])).toBe(0); }); // Run: ✗ FAIL - sum is not definedRED 阶段的硬性要求:
- 一次只写一个测试(One test at a time)
- 最小作用域(Minimal scope)
- 清晰的失败信息(Clear failure message)
- 观察到红色(Observe the red)
注意失败信息本身的含义:此刻sum is not defined,说明失败源自"行为尚不存在"这一事实。如果换成开发环境里已经跑得通的完整实现,你就不可能观察到这个真正有信息量的失败。这正是 Iron Law 2 要求"失败信息有意义"的原因。
GREEN:实现最简单的通过代码
// Write only enough code to pass this specific test function sum(numbers: number[]): number { return 0; } // Run: ✓ PASSGREEN 阶段的硬性要求:
- 最简实现(Simplest possible implementation)
- 不加多余功能(No extra features)
- 不做优化(No optimization)
- 只求通过(Just make it pass)
这一阶段极其考验自律:return 0看起来"不够正确",但在 RED-GREEN-REFACTOR 的纪律下,它恰恰是此时唯一正确的答案——它让一个失败测试通过,而新增任何代码(例如立刻实现reduce)都违背了 YAGNI 原则,属于越权行为。真正让sum变得完整的是下一轮 RED 阶段的增量测试,而不是本轮 GREEN 阶段的预判。
REFACTOR:保持绿色地改进
// Now improve the code while tests stay green function sum(numbers: number[]): number { return numbers.reduce((acc, n) => acc + n, 0); } // Run: ✓ PASS (still)REFACTOR 阶段的硬性要求:
- 测试必须保持绿色(Tests must stay green)
- 消除重复(Remove duplication)
- 提升可读性(Improve clarity)
- 不引入新功能(No new functionality)
重构是受保护的行为:因为前面两阶段已经建立了行为基线(测试全部通过),重构可以在安全网内进行,一旦重构破坏了任何测试,立即就能被发现。这与 skills/test-master/SKILL.md 中"使用有意义的it('…')描述、断言具体结果而非真值"等约束是一致的——只有当测试精确锁定行为,重构才不会悄悄改变语义。
循环在 Agent 工作流中的意义
在 MODELCLAUDE.md 的 Testing Mandate 一节中,RED-GREEN-REFACTOR 被概括为三个步骤并作为项目级行为约束:先写最小失败测试、实现最简通过代码、保持测试绿色地重构。对于 Agent 而言,这一循环还起到了防幻觉的作用:它把"我猜这段代码正确"替换为"测试先失败、后通过"的可验证证据,从而与仓库中"用证据而非假设说话"的整体哲学一脉相承。
四、必须拒斥的常见合理化借口
违反 TDD 之前,人们通常会给自己找一套"合理解释"。原文档以表格形式列出了六种最典型的心态,并逐一指出其谬误所在。这套清单在 Agent 协作场景中同样关键,因为 Agent 也极易落入"手动验证很快""先写代码后补测试"的捷径思维:
| 合理化借口 | 为什么是错的 |
|---|---|
| "我可以快速手动测试一下" | 手动测试无法防止回归(Manual testing doesn't prevent regression) |
| "我先写代码,之后再写测试省时间" | 你会跳过边界用例,并且测试会反过来迁就实现(You'll skip edge cases and test implementation) |
| "这太简单了,不需要测试" | 简单代码也会变;测试记录的是期望行为(Simple code changes; tests document expectations) |
| "代码都写完了,不能现在删掉" | 沉没成本谬误;删掉重来(Sunk cost fallacy; delete it) |
| "我知道它能跑,我以前做过" | 你的记忆不是文档(Your memory isn't documentation) |
| "我们在赶时间" | 技术债的代价比 TDD 更高(Technical debt costs more than TDD) |
在 research/superpowers.md 中,同样的拒绝清单还补充了两条延伸原则:Behavior-First Thinking(先问"它应该做什么",而不是"它做了什么"——事后补测试会被既有实现带偏)以及Never Rationalize(永远不要"就这一次"地跳过 TDD)。一旦允许一次例外,例外就会成为常态。
此外,与 TDD 铁律配套的反模式文件(skills/test-master/references/testing-anti-patterns.md)进一步解释了"为什么事后补测试必然劣化":测试可能退化为"测试 mock 而非测试行为"、生产类被塞入仅供测试使用的重置方法、mock 数据不完整导致"测试通过、生产崩溃"等典型问题。这些反模式本质上是违背铁律后自然发生的漂移,因此在拒斥借口的战斗中,始终要把反模式清单放在手边作为对照。
五、实战应用:新功能与 Bug 修复
场景一:从零开始一个新功能
原文档以UserValidator为例展示了完整的多轮循环。我们将其整理为清晰的四步节奏:
// 1. RED: Write failing test for simplest behavior describe('UserValidator', () => { it('should reject empty email', () => { expect(validateEmail('')).toBe(false); }); }); // 2. GREEN: Implement minimal passing code function validateEmail(email: string): boolean { return email.length > 0; } // 3. RED: Add next failing test it('should reject email without @', () => { expect(validateEmail('invalid')).toBe(false); }); // 4. GREEN: Extend to pass both tests function validateEmail(email: string): boolean { return email.length > 0 && email.includes('@'); } // Continue cycle...注意每一步的增量都非常小:行为(测试)先行,实现(代码)随后,每个测试都先失败再通过。整个过程持续迭代,直到行为被充分覆盖——包括空输入、非法格式等边界与错误路径。
场景二:修复一个 Bug
Bug 修复是 TDD 最有力的应用场景之一,因为它天然满足铁律:bug 就是"测试先失败"的现成素材。
// 1. RED: Write test that exposes the bug it('should handle negative numbers in sum', () => { expect(sum([-1, -2, -3])).toBe(-6); }); // Run: ✗ FAIL - got 0 instead of -6 // 2. GREEN: Fix the bug function sum(numbers: number[]): number { return numbers.reduce((acc, n) => acc + n, 0); } // Run: ✓ PASS // Bug is now fixed AND protected against regression修复之后的效果正如原文档所强调的:bug 不仅被修复,而且从此被测试保护,不会再以回归的形式复发。这恰好补上了第一节中"手动测试无法防止回归"的短板——回归保护是自动化测试相对手动验证的根本优势,而 TDD 确保这条回归测试先于修复代码存在。
这一"先写暴露 bug 的测试,再实施修复"的顺序,也与 MODELCLAUDE.md 中系统性调试(Systematic Debugging)流程的第四阶段一致:Implementation - Failing test first, then fix(先失败测试,再修复)。也就是说,在 claude-skills 的项目纪律中,即使是在调试场景下,测试优先的顺序依然不可动摇。
六、完成判定:验证清单
原文档给出了声明"代码已完成"之前的核对清单。任何声称"做完了"的说法都必须先逐项通过这份清单:
- 每个生产函数都有对应的测试
- 每个测试都写在其实现之前
- 每个测试都被观察到先失败
- 测试验证的是行为,而非实现细节
- 重构过程始终保持所有测试绿色
- 不存在没有测试的生产代码
最后一项尤其值得注意:"每个测试验证行为而非实现细节"意味着测试应断言可观察的输出(例如expect(validateEmail('')).toBe(false)),而不是断言内部方法调用序列。这与 skills/test-master/SKILL.md 中 MUST NOT 条款"测试实现细节(内部方法调用)——应测试可观察行为"完全一致,也与反模式文件中对"仅断言 mock 被调用"的批判互相印证。
七、在 test-master 与项目中如何落地
TDD 铁律在 claude-skills 仓库中不仅是一份独立参考文档,而是通过三层结构被制度化地落地:
第一层:test-master 技能入口。skills/test-master/SKILL.md 的 Reference Guide 表格将 TDD Iron Laws 列为按需加载的参考主题(Load when "TDD methodology, test-first development, red-green-refactor"),并在技能约束中显式要求"测试快乐路径 + 错误/边界路径"(如空输入、null、边界值)。这意味着当触发条件满足时,Agent 会被引导去加载本铁律文档并严格执行。
第二层:全局行为契约。MODELCLAUDE.md 的 Testing Mandate 把"没有先失败的测试,就没有生产代码"提升为项目级铁律,覆盖所有技能与所有工作流阶段。这使得"先测试后代码"成为仓库内 Agent 的默认行为,而不是某个技能的可选建议。
第三层:配套参考文档。skills/test-master/references/testing-anti-patterns.md 从反面给出五类典型违规与对应修复方法;skills/test-master/references/unit-testing.md 提供 Jest/Vitest 与 pytest 的具象写法(describe/it、fixture、mock 与 spy);skills/test-master/references/qa-methodology.md 则将 TDD 与 shift-left 理念衔接,把"单元测试随代码编写"列为早期测试活动的第一项,并给出了从"保存即跑(<5 分钟)"到"夜间回归(<2 小时)"的反馈周期目标。
渊源与整合记录。这份 TDD 铁律文档明确标注改编自 obra/superpowers(Jesse Vincent,MIT License)。research/superpowers.md 记录了其原始形态(三条铁律、RED-GREEN-REFACTOR、常见合理化借口),而 research/superpowers_research_findings.md 则记录了本项目将其提取并整合为独立参考文档的过程(Issue #56 已归档完成),这为想要追溯完整上下文或调研其他待整合议题的读者提供了清晰的线索。
结语
TDD 铁律的价值不在于它"看起来严格",而在于它把"质量"从主观感受变成了可观察的过程事实:先失败、后通过,每一行生产代码都能回溯到一条曾经失败的测试。在 claude-skills 的 test-master 中,这套纪律与反模式清单、单元测试模式、shift-left 方法论和全局 Testing Mandate 相互咬合,形成了一套对人和 Agent 同样有效的质量防线。下一次当你产生"这很简单不用测试""先跑通再说"的念头时,请对照本文第四节的理由清单——那正是铁律被突破前的最后信号。
【免费下载链接】claude-skills67 Specialized Skills for Full-Stack Developers. Transform Claude Code into your expert pair programmer.项目地址: https://gitcode.com/GitHub_Trending/claud/claude-skills
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考