四、TDD实战:AI助手的测试驱动开发强制执行

3 阅读6分钟

测试驱动开发(TDD)是一个简单概念:先写测试,再写代码。但实践中,我们经常偷懒。"这个太简单了,不需要测试"、"先写代码看看效果,测试后面补"、"我已经手动测试过了"。

Superpowers的TDD技能不给你偷懒的机会。这不是建议,是强制执行。

TDD的铁律

Superpowers的TDD技能有一个Iron Law:

NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST

违反这条规则的后果:

Write code before the test? Delete it. Start over.

No exceptions:
- Don't keep it as "reference"
- Don't "adapt" it while writing tests
- Don't look at it
- Delete means delete

这不是夸张。AI会执行这个规则。

RED-GREEN-REFACTOR流程

TDD的流程是:

  1. RED:写一个失败的测试
  2. Verify RED:运行测试,看它失败(确认失败原因正确)
  3. GREEN:写最小代码让测试通过
  4. Verify GREEN:运行测试,看它通过
  5. REFACTOR:清理代码(保持测试通过)
  6. Repeat:下一个测试

每一步都需要验证。特别是"看测试失败"这一步——这不是仪式,是证明测试有效。

为什么"看测试失败"是必要的

假设你写了一个测试,直接运行,它通过了。你可能想:"很好,测试通过了,可以写代码了。"

但问题是:测试通过了,你不知道它测试了什么。可能是:

  • 测试写错了,测试了错误的东西
  • 测试太宽松,没有测试关键逻辑
  • 测试依赖已有代码(你在重构已有项目)

看测试失败是验证:这个测试确实在测试缺失的功能。失败原因是"功能不存在",不是"测试写错了"。

只有看到测试因正确的理由失败,你才能确定:当你实现功能后,测试通过意味着功能正确。

一个实际案例:添加重试功能

我们看一个Superpowers中的TDD执行过程。

需求:实现一个重试操作的功能,失败时最多重试3次。

传统方式(没有TDD)

开发者可能直接写代码:

async function retryOperation(fn, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (e) {
      if (i === maxRetries - 1) throw e;
    }
  }
}

然后手动测试几次,看起来正常,就认为完成了。

问题:

  • 边界情况不确定:第一次成功呢?每次都失败呢?
  • 没有自动化验证:以后改代码可能破坏功能
  • 不确定行为:重试次数是3还是maxRetries?

Superpowers方式(强制TDD)

AI不会直接写代码。它先写测试:

RED阶段

test('retries failed operations 3 times', async () => {
  let attempts = 0;
  const operation = () => {
    attempts++;
    if (attempts < 3) throw new Error('fail');
    return 'success';
  };

  const result = await retryOperation(operation);

  expect(result).toBe('success');
  expect(attempts).toBe(3);
});

运行测试:

npm test

FAIL: ReferenceError: retryOperation is not defined

AI确认:

Test fails. Failure reason: retryOperation not defined.
This is correct failure: feature missing, not test error.
Proceed to GREEN.

如果测试失败原因是"test syntax error",AI会修复测试,重新进入RED。只有失败原因是"功能缺失",才进入GREEN。

GREEN阶段

AI写最小实现:

async function retryOperation(fn) {
  for (let i = 0; i < 3; i++) {
    try {
      return await fn();
    } catch (e) {
      if (i === 2) throw e;
    }
  }
  throw new Error('unreachable');
}

注意:这是"最小"实现。没有maxRetries参数(下一个测试才会要求),没有错误类型检查(还没测试),只有让当前测试通过的最小代码。

运行测试:

npm test

PASS

REFACTOR阶段

当前实现已经最小,不需要重构。如果有重复代码或命名问题,AI会在这里清理,但保持测试通过。

下一个测试

AI继续:

test('succeeds immediately without retries', async () => {
  let attempts = 0;
  const operation = () => {
    attempts++;
    return 'success';
  };

  const result = await retryOperation(operation);

  expect(result).toBe('success');
  expect(attempts).toBe(1);
});

RED → GREEN循环继续。

常见"合理化"及其反驳

Superpowers的TDD技能收集了开发者常见的"合理化":

ExcuseReality
"Too simple to test"简单代码也会出错。测试需要30秒。
"I'll test after"测试后通过不能证明什么。
"Tests after achieve same goals"测试后验证"写了什么",测试前定义"应该写什么"。
"Already manually tested"手动测试没有记录,不能重复运行。
"Deleting X hours is wasteful"保留不可信代码是更大的浪费。
"Keep as reference, write tests first"你会偷看参考。删除是必要的。

AI不会接受这些合理化。每次你(或AI自己)提出这些理由,技能会引用反驳:

"Too simple to test" → Simple code breaks. Test takes 30 seconds.

Red Flags:自检信号

技能有一个Red Flags列表,让AI自检:

- Code before test
- Test after implementation
- Test passes immediately
- Can't explain why test failed
- Tests added "later"
- Rationalizing "just this once"
- "I already manually tested it"
- "Tests after achieve the same purpose"
- "It's about spirit not ritual"
- "Keep as reference" or "adapt existing code"
- "Already spent X hours, deleting is wasteful"
- "TDD is dogmatic, I'm being pragmatic"
- "This is different because..."

All of these mean: Delete code. Start over with TDD.

如果AI意识到自己在合理化,它会停止,删除已有代码,重新开始TDD流程。

为什么AI能严格执行TDD

关键:AI没有人类的弱点。

人类弱点

  • 倾向:想快点完成,测试看起来浪费时间
  • 情感:删除已有代码感觉是浪费
  • 合理化:"这次可以例外"、"这不适用于简单代码"

AI特点

  • 没有时间压力感受:它不觉得测试"浪费时间"
  • 没有沉没成本感受:删除代码不感觉"浪费"
  • 有明确规则:技能告诉它必须做什么

但AI有另一个弱点:它想帮你。如果技能没有明确禁止,AI可能会迁就你的"跳过测试"请求。

Superpowers的设计是:技能明确禁止所有例外路径。AI看到规则,执行规则。结果是AI成为TDD的严格执行者。

实际观察

在第一个项目的任务2中,我观察到AI执行TDD:

每个函数都经过:

  1. 写测试
  2. 运行测试,记录失败原因
  3. 确认失败原因是"功能缺失"
  4. 写最小代码
  5. 运行测试,确认通过
  6. 自我检查:对照任务要求验证

没有一次跳过。没有一次"先写代码后补测试"。没有一次"这个太简单不需要测试"。

这就是强制执行的效果。

验证清单

Superpowers的TDD技能有一个验证清单,每次任务完成前检查:

- Every new function/method has a test
- Watched each test fail before implementing
- Each test failed for expected reason (feature missing, not typo)
- Wrote minimal code to pass each test
- All tests pass
- Output pristine (no errors, warnings)
- Tests use real code (mocks only if unavoidable)
- Edge cases and errors covered

如果任何一个checkbox不能check,AI知道自己跳过了TDD,必须重来。

结论

Superpowers的TDD不是建议,是强制执行。

核心机制:

  • Iron Law:没有生产代码没有失败测试在前
  • RED-GREEN-REFACTOR循环,每步验证
  • 合理化反驳表,阻止"这次可以例外"
  • Red Flags自检,让AI意识到合理化
  • 验证清单,完成任务前确认

结果是:AI不迁就你的偷懒倾向,强制执行TDD流程。这不是限制,是保护——保护你免于写出不可信的代码。

下一步

下一篇《调试不再盲目:系统化调试的思维转变》将展示Systematic Debugging技能:

  • 四阶段流程:Observe → Hypothesize → Isolate → Fix
  • 为什么猜测式调试是时间黑洞
  • AI如何帮助你建立证据链