返回

Superpowers test-driven-development:AI 不是不懂 TDD,是不愿意

AI 在 prompt 里被告知用 TDD 时几乎一定会作弊:先写实现再倒推测试。Superpowers 的 test-driven-development skill 通过把'测试必须先 FAILED'写成可观察事件,强制 AI 走真实的 RED-GREEN-REFACTOR。本文对比自由 TDD 与强约束 TDD 的实际行为差异,以及强约束在哪些场景反而拖累。

系列目录:Superpowers 教程系列索引

一、决策上下文

你跟 Claude 说"用 TDD 帮我实现这个功能"。它确实给你写了测试,也写了实现,最后还跑了一遍说全绿。看起来一切正常。

但你点开 git 历史会发现:测试和实现是同一次 commit 进来的,时间戳间隔几秒。它没经历过 RED。它先在脑子里写完了实现,然后倒推一份能通过的测试贴上。

这不是 AI 不懂 TDD。它知道 RED-GREEN-REFACTOR 的定义,能背书,能给你画完整流程图。它不愿意按这个顺序工作。原因也好理解:它的训练目标是"快速产出可工作的代码",TDD 要求的"先看一次失败"在 token 预测视角下是一笔无意义的浪费。

Superpowers 的 test-driven-development skill 解决的就是这件事。它不是在教 AI 什么是 TDD,是在阻止 AI 跳过 RED 阶段。

二、方案 A:让 AI 自由发挥的 TDD

我先描述不用 skill 时的实际行为。你给 Claude 的 prompt 是"用 TDD 实现一个标签验证类,名字不能为空、长度 1-50、只允许字母数字连字符"。

它的输出大概率长这样:

def test_validation():
    assert Tag("python").name == "python"
    with pytest.raises(ValidationError):
        Tag("")
    with pytest.raises(ValidationError):
        Tag("a" * 51)
    with pytest.raises(ValidationError):
        Tag("test@bad")

class Tag:
    def __init__(self, name):
        if not name or len(name) > 50 or not re.match(r'^[\w\s-]+$', name):
            raise ValidationError(...)
        self.name = name

测试和实现一次性给你。表面上这是 TDD 的产出。实际上它做的是反过来的过程:先在脑子里把实现想清楚,再写一组刚好覆盖这个实现的测试。我把这种叫"事后 TDD"。

事后 TDD 的问题不是结果不对,是它丢了 TDD 真正的价值。TDD 的设计反馈来自"先写测试时发现 API 难用",你写到一半才意识到入参奇怪、依赖复杂、副作用难 mock,于是回头改设计。AI 不经历这一步,所以它不会替你发现"name 这个字段叫 raw_name 更合适"或者"应该返回结果对象而不是抛异常"这种设计层面的反馈。它给你的是一个看着像 TDD 产出、本质是后端开发的代码。

我最早怀疑这件事时做过一个粗糙的实验:让 Claude 用 TDD 实现某功能,然后让另一个会话只看到测试代码,反推"按这组测试推断要实现的功能边界"。反推出来的功能描述比我原始需求要窄。事后 TDD 的测试是实现的影子,不是需求的影子。

三、方案 B:Superpowers 的强约束 TDD

Superpowers 的 test-driven-development skill 的 SKILL.md 里有几条像看门狗的硬规则:

先写测试。测试失败后才能写实现。只写刚好通过测试的代码。通过后必须重构。

这几条单独看是 TDD 的常识。关键在于它们被写进了一个 Claude 在每次开始写代码前必读的文件,通过 progressive disclosure 机制激活(详见上一篇 Superpowers Skills 拆解)。

这意味着实际执行流程变成了:

flowchart LR
    A[用户提需求] --> B[加载 TDD skill]
    B --> C[写第一个测试]
    C --> D[运行测试看到 FAILED]
    D --> E[FAILED 现在是日志里的事件]
    E --> F[写最少代码通过]
    F --> G[运行测试看到 PASSED]
    G --> H[决定要不要重构]

E 这一步是关键。在自由 TDD 里,“测试失败"是 AI 脑内的一个概念。在强约束下,“测试失败"是终端里实际跑出来的 FAILED 输出,是会话里能引用的真实事件。AI 不能跳过它,因为下一步实现要引用这次失败作为前置条件。

这个差别看着小,落地效果完全不同。

四、对比维度

维度 自由 TDD Superpowers 强约束 TDD
RED 阶段 跳过(脑内进行) 必须运行测试看到 FAILED
测试覆盖思路 围绕已有实现倒推 从需求边界正向展开
设计反馈 几乎没有 API 难写时会被迫修需求
重构步骤 经常省略 是独立一个阶段
失败模式 结果对但丢了过程 慢但纪律性可观察

我最初以为事后 TDD 只是顺序问题,跑得快慢不同。对比下来发现不是。丢的是设计反馈和边界覆盖,不是速度。

测试覆盖思路这一行最值得展开。事后 TDD 写的测试覆盖率可能很高,但测试边界跟实现的边界一样宽。实现忘了考虑的 case,测试也不会覆盖。比如上面那段实现,AI 没想到"全空格字符串 ' '” 该不该算空,所以测试里也没这个 case。强约束 TDD 下,AI 写测试时是从需求文本出发的,更可能列出"边界条件清单"再写测试,发现"哦,' ' 这个 case 需求里没说,要先问用户”。

设计反馈这行更微妙。我观察过几次实际跑:当强约束 TDD 跑到 Cycle 3、Cycle 4 时,AI 会主动说"这个测试写起来很痛,可能 API 设计有问题,我建议把 Tag(name=...) 改成 Tag.from_input(...) 返回 Result 类型"。这种反馈在自由 TDD 里几乎不会出现,因为它没经历过"写测试时发现 API 难用"的痛。约束让它被迫感受这种痛。

重构步骤最容易被忽略。自由 TDD 给你的代码通常是 GREEN 阶段产物:能跑、丑、变量名随意。强约束流程里 REFACTOR 是单独一阶段,AI 必须主动重命名、提炼方法、消除重复。我跑下来的代码可读性肉眼可见地高一档。

五、我的选择和实际用法

我现在做日常项目时默认开 strict 模式的 TDD skill:

tdd:
  enforcement: strict
  framework: pytest
  run_tests:
    - before_implementation
    - after_implementation

before_implementation 这条是关键。它强制 AI 在写实现前先把测试跑一遍。这就是让 FAILED 成为可观察事件的机制。

慢吗?慢。一个原本 AI 一次输出能搞定的功能,强约束下要拆成 3-5 个 cycle,每个 cycle 都要等 pytest 跑完。我目测平均慢 2-3 倍,没精确测过。

但产出的代码我更敢直接合进 main 分支。自由 TDD 给我的代码我还会再读一遍测试,怀疑测试是不是按实现倒推写的。强约束 TDD 给我的代码至少过程上是诚实的,commit history、终端日志、cycle 之间的对话片段都能复盘。

跑久了之后还有一个副产品:我反而开始喜欢这种慢。它逼我把需求拆得更细。原本一句话扔给 Claude 让它自己理解的功能,强约束 TDD 下我必须先把"第一个 cycle 测什么"想清楚,等于先把需求拆了一遍。这种拆需求的训练效果不在 skill 的设计目标里,但价值不低。

六、什么时候反过来选

我并不在所有场景都开 TDD skill。有几类情况开了反而拖累。

探索式编程。我还不确定这套设计对不对,想先快速做一版能跑的看看效果。这时 TDD 的"测试约束实现"反而锁死了尝试不同方向的自由度。先跑通,再补测试,更合适。

性能调优。“这段代码应该多快"很难写成断言。强行 TDD 会得到一堆 assert duration < 100ms 的脆弱测试,跑在不同机器上结果不一样。性能问题该有专门的 benchmark,不是 unit test。

调外部不确定系统。集成第三方 API 时强制 TDD 会逼出大量 mock,最后测的是 mock 而不是真行为。文档说返回 A,但他们偶尔返回 B,你的 mock 永远不会发现。这种代码我宁可写一个端到端冒烟测试,不走 RED-GREEN-REFACTOR。

TDD skill 在"需求清楚、实现复杂、可重复运行"的代码里效果最好。不是哪都该开,是该挑场景开。

七、留白

我还没想清楚的:当模型继续变强,能不能像顶级工程师那样主动选择 RED-GREEN-REFACTOR 而不需要外部强制?理论上可以,但前提是它的训练目标里要加入"过程纪律"作为奖励信号。这跟当前主流的"输出质量奖励"是另一个维度的事,不是加 prompt 能解决。

短期内我会继续用 skill 强约束。如果你试过别的让 AI 自觉做 TDD 的方法,告诉我。


系列导航

最后更新于 2026年05月09日 00点00分00秒