跳到正文

TDD 测试驱动开发

使用 Claude 进行 TDD(测试驱动开发)需要明确的提示词。Claude 天然会先写实现,再写测试。TDD(测试驱动开发)要求相反的顺序。


目录

  1. TL;DR
  2. 问题所在
  3. 配置
  4. 红-绿-重构循环
  5. 与 Claude Code 功能集成
  6. 反模式
  7. 进阶模式
  8. 延伸阅读

TL;DR

Plain
红 → 绿 → 重构

但你必须明确提示 Claude:
"为 [功能] 编写一个失败的测试。暂不编写实现。"

问题所在

没有明确指令时,Claude 会:

  1. 编写实现代码
  2. 然后编写能通过该实现的测试

这违背了 TDD(测试驱动开发)的目的:测试应该驱动设计,而非验证已有代码。


配置

CLAUDE.md 配置

添加到你的项目 CLAUDE.md:

Markdown
## 测试约定

### TDD(测试驱动开发)工作流
- 始终在实现前编写失败的测试
- 使用 AAA 模式:Arrange(准备)-Act(执行)-Assert(断言)
- 每个测试尽量只有一个断言
- 测试名称描述行为:"should_return_empty_when_no_items"

### 测试优先规则
- 当我要求实现功能时,先写测试
- 测试最初应该失败(无实现存在)
- 只有在测试编写完成后,才实现让测试通过的最少代码

自动运行测试的钩子(可选)

创建 .claude/hooks/test-on-save.sh

Bash
#!/bin/bash
# 测试文件变更时自动运行测试
if [[ "$1" == *test* ]] || [[ "$1" == *spec* ]]; then
  npm test --watchAll=false 2>&1 | head -20
fi

红-绿-重构循环

第一阶段:红(编写失败的测试)

提示词

Plain
为 [功能描述] 编写一个失败的测试。
暂不编写实现。
测试应该失败,因为函数/方法不存在。

示例

Plain
为一个计算购物车商品总价的函数编写失败的测试,
如果总价超过 100 元则应用 10% 折扣。
暂不实现该函数。

预期 Claude 行为

  • 创建包含测试用例的测试文件
  • 测试引用不存在的函数
  • 运行测试会失败,提示「函数未定义」或类似错误

验证

Bash
npm test  # 应该失败,提示「calculateCartTotal is not defined」

第二阶段:绿(最小实现)

提示词

Plain
现在实现让这些测试通过的最少代码。
只写足够通过当前测试的代码,不要多。

预期 Claude 行为

  • 创建实现文件
  • 编写满足测试的最少代码
  • 避免过度工程化

验证

Bash
npm test  # 应该通过

第三阶段:重构(清理代码)

提示词

Plain
重构实现以提升代码质量。
重构后测试必须保持绿色。
重点关注:[可读性 / 性能 / 消除重复]

预期 Claude 行为

  • 在不改变行为的前提下改善代码
  • 运行测试验证仍能通过
  • 记录任何重大变更

与 Claude Code 功能集成

配合 TodoWrite

在任务列表中追踪 TDD(测试驱动开发)阶段:

Plain
用户:「使用 TDD(测试驱动开发)实现用户认证」

Claude 创建待办事项:
- [ ] 红:为登录编写失败的测试
- [ ] 绿:实现登录以通过测试
- [ ] 重构:清理登录实现
- [ ] 红:为登出编写失败的测试
- [ ] 绿:实现登出
- [ ] 重构:清理

配合计划模式

使用计划模式规划测试策略:

Plain
[按 Shift+Tab 进入计划模式]

我需要用 TDD(测试驱动开发)实现一个购物车。
在我们开始写任何代码之前,先规划测试用例。

Claude 会以只读方式探索代码库,然后在任何实现之前提出测试计划。

配合 Hooks(钩子)

使用工具后钩子在编辑后自动运行测试:

JSON
// 在 .claude/settings.json 中
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "npm test --watchAll=false 2>&1 | head -20"
      }
    ]
  }
}

配合子智能体

将测试编写委托给专注范围的智能体:

Plain
使用 test-writer 智能体为 UserService 类创建全面的测试,
覆盖所有边缘情况。
然后我会按照这些测试来实现。

反模式

验证缺口

验证缺口是智能体在验证套件确认之前就报告功能完成的失败模式。这是多会话智能体工作中最常见的可靠性故障,完全可以通过正确的框架设计来预防。

三个可观察症状:智能体在任何测试命令运行之前就打印成功消息;测试运行但 stderr 被丢弃或未读取;仅单元测试通过,而验收标准规定了端到端行为。

修复方案是一个三层验证栈,在功能被标记为 passing 之前必须全部通过:

  1. 代码检查 — 语法和风格检查(最快,在运行测试前捕获明显错误)
  2. 单元测试和集成测试 — 各组件的功能正确性
  3. 端到端测试 — 用户或外部调用者所见的行为契约

每一层捕获不同类别的故障。单元测试可以通过,而组件边界会断裂。端到端测试能发现单元测试看不到的状态传播错误和生命周期问题。跳过任何一层都会留下缺口。

独立评估者原则:编写代码的智能体不能是同一个调用来认证其完成。这不是对模型的不信任;而是关于上下文如何影响评估。一个刚花了两个小时构建功能的智能体会对模糊输出作出慈善解释。一个独立读取退出代码的工具后钩子或第二个智能体则不会。examples/hooks/bash/verification-gate.sh 中的钩子实现了这个模式。

Anthropic 在其框架设计研究中记录了这个故障:在裸运行(无框架)中,他们的智能体在 20 分钟后报告游戏编辑器完成。什么都不工作。向框架添加独立评估者后,同一模型运行了 6 小时并交付了可用结果。(来源:https://www.anthropic.com/engineering/harness-design-long-running-apps

WIP=1 规则与此相关:每次只保持一个功能处于 active 状态,意味着验证缺口发生时,只影响一个功能,而非同时影响多个。

不该做的事

反模式错误原因正确做法
「为这个功能写测试」Claude 会先实现「写尚不存在的失败测试」
「添加测试和实现」失去测试优先的好处分成两个提示词
「确保测试通过」鼓励先实现「写测试,然后最小化实现」
跳过重构阶段积累技术债务绿色后始终重构
同时处理多个功能失去专注每个 TDD(测试驱动开发)循环一个功能

常见错误

错误:要求 Claude「测试」现有代码。

Plain
# 错误
「为现有的 calculateTotal 函数写测试」

# 正确
「假设函数不存在,为 calculateTotal 的行为写测试。
然后我们验证现有实现是否通过。」

错误:合并红色和绿色阶段。

Plain
# 错误
「带测试实现 calculateTotal」

# 正确
「为 calculateTotal 写失败的测试。到此为止。」
[测试写完后]
「现在实现以通过这些测试。」

进阶模式

基于属性的测试

Plain
为排序函数编写基于属性的测试。
需要测试的属性:
- 输出长度等于输入长度
- 所有输入元素都存在于输出中
- 输出是有序的
使用 fast-check 或类似库。

变异测试

Plain
测试通过后,运行变异测试来发现薄弱点。
识别无法捕获变异的测试。

更进一步:JiTTesting 在 PR 时自动应用变异测试——由 LLM 生成,短暂存在,零维护。Meta 大规模部署了这项技术,与传统测试相比回归捕获率提升了 4 倍。参见 Meta 的即时捕获测试生成和「方法论指南」,了解当今 Claude Code 的近似模式。

遗留代码的 TDD(测试驱动开发)

Plain
我需要重构 legacyFunction。
首先,编写记录当前行为的特征测试。
然后我们可以有信心地重构。

示例会话

用户请求

Plain
使用 TDD(测试驱动开发)实现一个 URL 缩短服务。

第一阶段:红

Plain
让我们使用 TDD(测试驱动开发)。首先,为以下情况编写失败的测试:
1. 缩短 URL 返回一个短代码
2. 检索短代码返回原始 URL
3. 无效 URL 被拒绝
4. 过期链接返回错误

暂不实现任何内容。

第二阶段:绿

Plain
测试已编写且失败。现在实现让它们通过的最少代码。
暂时使用内存存储。

第三阶段:重构

Plain
测试已通过。现在重构:
- 将 URL 验证提取到独立函数
- 添加合适的错误类型
- 改善变量名

每次修改后运行测试以确保保持绿色。

延伸阅读

  • 「../core/methodologies.md」 — 完整方法论参考
  • 「紧密反馈循环」 — 第 9.5 节
  • 「examples/skills/tdd-workflow.md」 — TDD(测试驱动开发)Skills(技能模块)模板
  • Anthropic 最佳实践
  • 「task-management.md」 — 使用 Tasks API 跨会话追踪 TDD(测试驱动开发)循环
  • Superpowers — 将 TDD(测试驱动开发)作为强制门控的插件套件:在失败测试存在之前编写的代码会被删除并从头重做。比手动提示词更严格的执行。

来源:飞书 · AI Spark 知识库 | 原文(最新版):https://lcnniolukk80.feishu.cn/wiki/TSlWwH7koiqK1vk1i2HcKWjpnIh | 归档:2026-06-04