跳到主要内容

TDD测试驱动开发:用测试为AI导航

本章要点

上一节,我们认识了 Vibe Coding——那种与 AI 共舞的编程新范式。Vibe Coding 极大地降低了编程门槛,让想法可以快速变成代码,但它也埋下了一个难题:当 AI 替你写下代码,你怎么知道它真的"对"了?

这一节,我们要认识一种专门解决这个问题的方法论。

这就是 TDD(Test-Driven Development,测试驱动开发)。它不是什么新鲜概念——从它诞生到现在,已经过去了将近三十年。但在 AI 编程的时代,这个古老的方法论竟然焕发出了全新的生命力。

读完这一节,你会获得:

  • 理解 TDD 的核心循环:红-绿-重构,以及每一步的意义
  • 知道为什么 TDD 在 AI 时代变得比以往任何时候都更加重要
  • 学会用测试"驯服"AI,让它不再偏离你真正想要的东西
  • 认识 Superpowers——一个把 TDD 变成强制执行工作流的 Claude Code 技能框架

TDD的前世:一个被推崇却经常被跳过的方法

在聊 TDD 和 AI 的关系之前,我们先回到 TDD 本身,看看它究竟是什么,以及为什么人们明明知道它好,却总是偷懒跳过它。

TDD 的思路可以用一句话概括:先写测试,再写代码。听起来很简单,但在实践中,这和大多数人的直觉完全相反。正常的开发流程是:想清楚要做什么 → 把代码写出来 → 写几个测试确认一下 → 收工。而 TDD 要求你把这个顺序翻转:先写一个描述"期望行为"的测试,让它失败(因为代码还不存在),然后写出能让测试通过的代码,最后清理代码让它更优雅,同时保持测试仍然通过。

这个循环有一个非常形象的名字:红-绿-重构(Red-Green-Refactor)

  • 红(Red):写一个测试,运行它,它理应失败——因为你要实现的功能还不存在。这一步的目的是确认测试本身是有意义的,而不是一个永远都会通过的空测试。
  • 绿(Green):写刚好够用的代码,让这个失败的测试变成通过。注意"刚好够用"四个字——不多写,不优化,只求通过。
  • 重构(Refactor):现在测试绿了,代码可以工作了。这时候你可以安心地整理代码结构、消除重复、改善命名,只要测试还是通过的,你的修改就是安全的。

TDD 的发明者肯特·贝克(Kent Beck)在 1990 年代将这一实践系统化,它成为了极限编程(Extreme Programming)运动的核心实践之一。从那以后,几乎所有的编程书籍和工程实践课程都会把 TDD 列为"最佳实践"。

可问题在于:真正执行 TDD 的人并不多。

原因并不难理解。写测试这件事,在你写好代码之后来做,感觉是在证明一件你已经知道答案的事。在项目截止日期压着的时候,谁有时间先写测试?更何况,在代码还不存在的时候,你要怎么描述它的行为?一些开发者会说:"等我把功能实现出来,再补测试。"然后等他们真的回来"补测试"的时候,往往已经是一周后了,或者永远不了。

TDD 就这样成了软件工程界最知名的"我知道应该做但就是没做"的实践之一。

直到 AI 进场,这个故事发生了转折。

AI时代:TDD为什么变得前所未有地重要

当你用 AI 来写代码时,你会遇到一个传统编程里几乎不会出现的问题:AI 生成的代码经常"看起来对,但实际上不对"

这不是 AI 的失误,而是 AI 的工作原理决定的。语言模型预测的是"在这段上下文之后,最可能出现的内容"——它非常擅长生成语法正确、逻辑流畅的代码,但它无法在每次生成时都精确理解你所有的隐性需求。它可能会悄悄地稍微改变了一个函数的行为,但整体代码还是能跑;它可能处理了你说的主要情况,却漏掉了你没提到的边界情况;它甚至可能在你的要求比较模糊时,做出了一个你当时没注意到、但后来很麻烦的设计决策。

这类问题,很多时候肉眼看代码是看不出来的。你需要一套机制,能在代码生成之后,快速、准确地告诉你:"这段代码到底有没有做到我要求的事?"

测试,就是这套机制。

但这里有一个关键的时序问题。如果你等 AI 生成完代码,再让它写测试,你会发现测试往往"为代码辩护"——AI 会写出刚好能让自己生成的代码通过的测试,而不是针对真实需求的测试。这就像请一个人审查自己写的合同,他会下意识地把有利于自己的条款保留下来,而不是站在你的立场上审查。

这正是 TDD 的精髓所在:在 AI 开始写代码之前,你就把期望的行为用测试描述出来。这样,测试就成了 AI 的"导航仪",而不是事后的"橡皮章"。

这种思路带来了三个具体的好处:

第一,测试是无歧义的需求规格。自然语言是有歧义的,"实现一个用户登录功能"可以有一百种解读。但一个具体的测试用例——"给定用户名'alice'和正确密码时,登录函数应返回 true;给定错误密码时,应返回 false;给定空用户名时,应抛出参数错误"——是完全没有歧义的。AI 看到这个测试,就知道它要做的是什么,而不是猜测你的意图。

第二,测试是即时反馈机制。AI 生成代码后,你立刻运行测试。通过了,说明 AI 确实理解了你的需求;失败了,说明 AI 有某个地方搞错了,你可以直接把失败的测试信息反馈给 AI,让它继续修正。这个循环可以在几秒钟内完成,比你人工阅读每一行代码要快得多。

第三,测试保护了你在重构时的安全感。AI 写出来的第一版代码,通常功能上正确但结构上粗糙。如果你有测试,你可以放心让 AI 去重构代码——改完之后,跑一遍测试,如果全绿,你知道重构没有引入新的问题。如果没有测试,每一次重构都是在走钢丝。

TDD与AI的具体协作:三步循环怎么走

理解了原因,我们来看具体的操作。在 AI 编程的语境下,TDD 的红-绿-重构循环每一步各有侧重。

红色阶段:你来写测试,AI来理解意图

在红色阶段,你的工作是用测试语言精确描述你的需求,然后要求 AI 验证这些测试是"正确失败"的——即,测试因为功能不存在而失败,而不是因为测试本身写错了而失败。

一个实用的方法是:把你的需求描述给 AI,让它帮你草拟测试用例,然后你审查这些测试用例,确认它们确实捕捉了你所有的需求(包括边界情况),再让 AI 运行它们,确认它们当前是失败的。

这一步的关键不在于测试怎么写,而在于让测试成为你和 AI 之间对需求的共识。一旦测试被你审查和确认,它就成了这个功能的规格——后续所有的代码生成都必须以"让这些测试通过"为目标,而不是以"让你感觉对了"为目标。

绿色阶段:AI来写代码,测试来验证

在绿色阶段,你把测试文件交给 AI,告诉它:"请写出能让这些测试通过的代码。只实现测试所要求的功能,不要多做。"

"不要多做"这四个字非常关键。AI 有一种强烈的倾向——为了"周全",它会在你没有要求的地方添加额外的逻辑、额外的参数、额外的功能。这些"额外"在短期看来是好心,但在长期看来是隐患:未经测试的代码是不受约束的代码,你不知道它会在什么时候产生意想不到的副作用。

用测试约束 AI 的范围,是让它保持"刚好够用"的最有效方式。

重构阶段:清理代码,测试保底

绿色阶段结束后,代码能工作,但可能不够优雅。这时候你可以把现有代码交给 AI,告诉它:"保持所有测试通过的前提下,请改善这段代码的结构——消除重复、改善命名、整理逻辑。"

测试在这一步是"安全网"。每做一个修改,跑一遍测试,如果还是全绿,这次修改就是安全的。如果有测试变红了,说明重构过程中引入了问题,AI(或你自己)需要立刻修复它。

这三步合在一起,构成了一个完整的循环。每完成一个小功能,就走一次红-绿-重构,然后开始下一个功能。一点一点地,你用测试把代码堆砌起来,每一块都是有保证的,整体也就越来越可信。

Superpowers:让TDD成为不可跳过的铁律

你已经理解了 TDD 和 AI 结合的逻辑,也了解了三步循环的操作方法。但有一个现实问题:这个流程需要人的纪律性来维持。你很容易在"没时间"或"功能比较简单"的时候,对自己说"这次就直接写代码吧",然后跳过红色阶段。AI 本身也是如此——你不明确要求它用 TDD,它会直接开始实现代码,而不是先写测试。

Superpowers 就是为了解决这个问题而生的。它不是建议你用 TDD,而是把 TDD 变成强制执行的工作流——就像道路上的减速带,不是告诉你要减速,而是物理上逼迫你必须减速。

Superpowers是什么

Superpowers 是开发者 Jesse(GitHub 用户名 obra)在 2025 年 10 月发布的一套 Claude Code 技能框架(Skills Framework)。它的核心概念是"技能"(Skills)——一系列存储在 Markdown 文件中的工作流规范,每当 Claude Code 检测到你准备开始某类任务时,它会自动加载并严格遵循对应的技能。

Skills 和提示词有一个本质区别:技能是强制性的,提示词是建议性的。如果你在会话开始时提醒 AI "请用 TDD",AI 会记得一段时间,然后随着对话展开慢慢"忘记"。但 Superpowers 的 TDD 技能不是"建议",它包含明确的铁律:在任何功能实现开始之前,必须先写测试;如果发现代码是在测试之前写的,必须删除;只有当测试确认失败之后,才允许开始写实现代码。

使用 Claude Code 安装 Superpowers 只需要两行命令:

/plugin marketplace add obra/superpowers-marketplace
/plugin install superpowers@superpowers-marketplace

重启 Claude Code 之后,你会在每个新会话的开始看到一条提示,告诉你 Superpowers 已经就位,相关技能已经准备好了。

Superpowers的核心工作流

Superpowers 把一个完整的功能开发过程编排成四个阶段,它们环环相扣,TDD 贯穿其中:

头脑风暴(Brainstorm)→ 制定计划(Plan)→ 执行任务(Execute)→ 测试驱动(TDD)

头脑风暴阶段:当你用自然语言描述一个想法——"我想做一个购物车功能"——Superpowers 会自动激活头脑风暴技能,阻止 AI 直接开始写代码。AI 会转而向你提问,一次一个问题,帮你厘清需求细节:支持哪些操作?数量上限是多少?用户未登录时如何处理?这个过程通常会揭示出你自己也没有想清楚的地方。

这一步的一个有趣设计细节是:如果你强行要求 AI 直接开始写代码,Superpowers 会拒绝,并把你引回头脑风暴阶段。Jesse 专门为此设计了"压力测试场景"——模拟"生产系统故障,每分钟损失5000美元"这样的紧迫情况,验证 AI 在极端压力下是否还会遵守技能规范。答案是:会的。

制定计划阶段:头脑风暴结束后,AI 会将讨论结果整理成一份详细的实现计划,按功能模块拆分成具体的、可独立执行的任务列表,并自动在 docs/plans/ 目录下保存这份计划。它还会自动创建一个 git worktree,让这个功能的开发工作在隔离的分支上进行,不会干扰其他并行进行的任务。

执行任务阶段:计划确认后,Superpowers 支持两种执行模式:

第一种是"人工协调模式",你同时开启两个 Claude Code 会话——一个扮演"架构师",一个扮演"实现者"。实现者按计划一步步推进,架构师负责审查每个阶段的成果,你扮演中间的协调人,在两者之间传递反馈。

第二种是"子代理自动化模式",这也是目前更推荐的方式。Superpowers 将任务列表分发给多个独立的子代理(Subagent),每个子代理负责实现一个独立任务,完成后自动触发代码审查,审查通过后再继续下一个任务。整个流程可以在几乎不需要人工干预的情况下自动推进。

TDD技能阶段:无论使用哪种执行模式,TDD 技能都贯穿在每一个任务的实现过程中。每个子代理在实现每项任务时,都必须严格遵循红-绿-重构循环:先写一个失败的测试,确认它确实失败了,再写实现代码让测试通过,最后重构。没有例外。

TDD技能如何真正起作用

Superpowers 的 TDD 技能之所以有效,在于它的设计非常具体,把每一步都明确化、强制化,消除了所有模糊地带。

技能文件里包含"反模式参考"——明确列出了 TDD 过程中最常见的错误,比如:在测试失败之前就开始写实现代码;为了让测试通过而写"作弊代码"(专门针对测试用例而不是真实逻辑);在重构阶段顺手添加了新功能而没有先写对应的测试。

每当 AI 代理检测到自己正在滑向这些反模式时,TDD 技能会触发一个"拉回"机制:停止当前操作,明确说明哪里偏了,然后回到正轨。

更有意思的是,Superpowers 的 TDD 技能本身,也是用 TDD 方式开发的。Jesse 在构建新技能时,会让 AI 在一组模拟子代理上"测试"这个技能——这些模拟子代理充当"未来的 AI 会话",验证技能是否表达清晰、是否能被遵守、是否在各种压力场景下依然有效。如果测试失败,Jesse 会修改技能文档,直到它能让模拟子代理在所有场景下都正确表现。

这是一种颇具哲学意味的递归:用 TDD 来确保 TDD 技能本身的质量

真实案例:chardet 7.0.0

Superpowers 的 TDD 技能带来了可量化的成果。

chardet 是一个著名的 Python 字符编码检测库。在 7.0.0 版本的开发过程中,开发团队完整地使用了 Superpowers 工作流,TDD 技能在整个过程中强制执行。最终结果是:

  • 性能提升 41 倍(是的,你没有看错)
  • 编码检测准确率达到 96.8%
  • 测试套件覆盖 2,161 个文件,横跨 99 种编码格式

测试套件的规模,是 TDD 技能的直接产物——因为每一个功能在实现之前都要先有测试,所以最终积累起来的测试覆盖是自然而然发生的,而不是在最后"补"上来的。

这个数字本身就说明了 TDD 和 AI 结合的可能性:当 AI 有了测试作为导航仪,当工作流有了技能作为强制执行机制,人类开发者的精力可以集中在"测试代表的是什么需求"这个最有价值的问题上,而把"代码如何实现"的细节交给 AI 处理。

小结

这一节,我们认识了测试驱动开发(TDD)在 AI 编程时代焕发的新生命力。

TDD 的红-绿-重构循环——先写失败的测试,再写让测试通过的代码,最后重构改善代码结构——并不是一个新概念。但在 AI 编程的语境下,测试从"可选的最佳实践"变成了"约束 AI、验证 AI 的必要机制"。没有测试,你只能靠感觉判断 AI 生成的代码是否真的对;有了测试,你有了客观、可执行的标准。

我们认识了 Superpowers——由开发者 Jesse 构建的 Claude Code 技能框架。它的核心设计思想是把 TDD 从"建议"变成"强制":头脑风暴阶段先把需求厘清,计划阶段把实现拆成任务,执行阶段由子代理按任务推进,TDD 技能在每一个任务的实现中铁律执行红-绿-重构循环。chardet 7.0.0 的案例——41 倍性能提升,覆盖 2,161 个文件的测试套件——是这套方法论落地有效的有力佐证。

下一节,我们会认识另一种方法论——SDD(规范驱动开发)。如果说 TDD 回答的是"实现正确了吗",SDD 回答的则是更上层的问题:"我们在构建正确的东西吗?"两者各司其职,相辅相成,共同构成了 AI 时代最严谨的工程实践。

练习

思考题 1:测试作为规格

想一个你最近做过的小功能,尝试用 TDD 的方式重新表达需求:只用测试用例来描述这个功能,不写任何代码,不写任何文字说明。

你能写出多少个测试用例?有没有在写的过程中发现了一些你之前没有想清楚的边界情况?

思考题 2:测试写得太晚 vs 测试写得太早

有些人批评 TDD,说"先写测试"要求你在最不了解代码结构的时候做最多的设计决策,这样写出来的测试反而很脆——代码一重构,测试就得跟着全改。

你认为这个批评有道理吗?有没有办法兼顾"测试先行"和"设计灵活"?

实践题 3:用测试约束AI

选一个简单的功能(比如"一个检查密码强度的函数"),尝试这样操作:

  1. 先不告诉 AI 要实现什么,只给它一组测试用例,要求它写出能让测试通过的代码
  2. 再用另一个会话,直接用自然语言描述需求,让 AI 自由实现
  3. 对比两次的结果:哪种方式生成的代码更符合你的期望?哪种方式的代码更容易出现你没预期的行为?

讨论题 4:Superpowers 的铁律是好事还是坏事

Superpowers 的设计选择是"把 TDD 变成不可跳过的铁律",甚至在压力场景下也不允许绕过。

你认为这种强制执行的设计有什么潜在的弊端?有没有一些场景下,跳过 TDD 其实是合理的——如果有,怎么在"纪律性"和"灵活性"之间找到平衡?