1. 为什么你的 TDD 总是跑不起来
TDD 的流程大家都背得出来:先写一个失败的测试,再写最少的代码让它通过,最后重构。但真正落到日常开发里,多数人的状态是——知道该写测试,却总是先写实现,测试留到"有空再补",然后永远没空。覆盖率长期卡在 30% 上下,改一行代码要手动跑一遍mvn test,看到红色报错再切回编辑器改,改完再跑,来回切换上下文,一个下午就没了。
我试过把 Claude Code 接进这个循环,让它承担"生成测试 → 运行失败 → 分析反馈 → 修复代码"这四个动作,人只负责审查和决策。实测下来,一个中等复杂度的 Service 类,从零测试到全绿,手动大概要 40 分钟,接上自动化循环后压缩到 8 分钟左右,而且测试风格统一、Mock 策略合理,不会出现"断言粗糙、依赖真实网络"这类坑。
这篇要交付的是一套可复制的配置:一份settings.json骨架、一个测试生成 Skill、一个写入即触发的 Hook,以及验证整条流水线跑通的命令。适合已经在用 Claude Code、想把 TDD 真正跑成闭环的后端或全栈开发者。读完你能在本地项目里直接落地,不需要改工具本体代码。
2. 前置准备:TaoToken 接入与 Claude Code 环境
Claude Code 本身是终端里的 AI 编程助手,要让它稳定跑 TDD 循环,第一步是把模型接入配好。我用的是 TaoToken 的接入方式,它提供兼容 Anthropic 的 API 端点,配置简单,适合本地和 CI 两种场景。
先拿到 API Key。打开控制台创建密钥:
https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite创建后复制sk-开头的密钥,接下来配置环境变量。Claude Code 读取ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY两个变量,指向 TaoToken 的 API 地址即可:
export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="sk-你的密钥"如果你希望这些变量在每次开终端时自动生效,写进~/.zshrc或~/.bashrc。Windows 用户用系统环境变量面板设置同名变量即可。
安装 Claude Code:
npm install -g @anthropic-ai/claude-code claude --version能打印出版本号就说明 CLI 装好了。接着验证模型连通性,跑一个最小请求:
claude -p "回复 ok" --output-format json返回 JSON 里result字段是ok,说明 API 接入正常。这一步很关键——后面所有自动化循环都依赖这个通道,如果这里不通,Hook 触发的修复请求会静默失败。
注意:API Key 不要硬编码进仓库文件。本地用环境变量,CI 里用 Secrets 注入,后面第 8 节会给具体做法。
3. 可复制配置:settings.json 骨架与 Skill 结构
Claude Code 的项目级配置放在.claude/settings.json,它决定权限、Hook 和工具白名单。下面这份骨架是我在多个项目里验证过的,直接改路径就能用。
{ "permissions": { "allow": [ "Read", "Grep", "Glob", "Write", "Edit", "Bash(mvn test:*)", "Bash(./gradlew test:*)", "Bash(npm test:*)", "Bash(pytest:*)" ], "deny": [ "Bash(rm -rf:*)", "Bash(git push:*)" ] }, "hooks": { "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [ { "type": "command", "command": "./scripts/auto-test.sh" } ] } ] } }几个要点解释一下。permissions.allow是白名单,只放测试运行相关的 Bash 命令,避免 AI 在修复时执行危险操作;deny里挡掉删除和推送,这是护栏。hooks.PostToolUse是关键——每当 Claude 写入或编辑文件,就触发auto-test.sh,实现"保存即测试"。
Skill 放在.claude/skills/tdd-automation/SKILL.md,用 YAML 元数据加 Markdown 正文。description字段决定 Claude 何时自动启用它,写得越具体匹配越准:
--- name: tdd-automation description: 自动化 TDD 循环,当用户要求为某个类或方法生成测试、运行测试并修复失败时启用 --- # TDD 自动化 Skill ## 工作流程 1. 分析目标代码,识别所有公共方法及其输入输出 2. 为每个方法生成单元测试,使用项目现有测试框架 3. 运行测试套件,捕获失败输出 4. 分析失败根因:编译错误、断言失败还是运行时异常 5. 修复实现代码,重复步骤 3-4 直到全绿 6. 全绿后执行重构,保持测试通过 ## 测试生成规范 - 每个测试用例独立,不依赖执行顺序 - Mock 所有外部依赖,不发起真实网络请求 - 覆盖正常路径、边界条件、异常路径 - 断言要具体,避免 assertNotNull 这类弱断言 ## 失败处理策略 - 编译错误:直接修复语法和类型问题 - 断言失败:对比预期值与实际值,判断是实现错还是测试错 - 运行时异常:检查依赖注入和配置加载Skill 采用渐进式披露:Claude 启动时只加载元数据,判断任务相关后才读完整正文,所以你可以把团队规范写得很细,不用担心撑爆上下文。
4. 测试生成:让 AI 先读懂你的规范
直接说"给这段代码写测试",得到的结果往往风格飘忽。正确做法是先让 Claude 理解项目结构,再按 Skill 规范生成。
在项目根目录启动 Claude Code:
claude进入交互后,先让它扫描项目,建立上下文:
> 阅读 pom.xml 和 src/test 下已有的测试文件,总结本项目使用的测试框架、断言库和 Mock 方式Claude 会读取依赖配置和现有测试,输出一份风格摘要。确认无误后,再触发测试生成:
> /skill tdd-automation > 为 src/main/java/com/example/UserService.java 生成完整单元测试并运行它会先分析UserService的公共方法,识别参数类型、返回值和分支逻辑,然后按 Skill 里的规范生成UserServiceTest.java。生成过程中,PostToolUseHook 会自动触发测试运行,你不需要手动敲mvn test。
如果项目较大,想批量生成,用 Headless 模式一行搞定:
claude -p "扫描 src/main/java 下所有未覆盖的公共方法,为每个方法生成单元测试,使用 JUnit 5 和 Mockito" \ --output-format json \ --allowed-tools Read,Grep,Glob,Write \ --max-turns 20--max-turns限制交互轮数,控制成本;--allowed-tools限定工具范围,这里不给 Bash,避免它在生成阶段就乱跑命令。
5. 失败反馈与代码修复:闭环怎么转起来
测试跑红之后,反馈要能回到 Claude 的上下文里,修复才有依据。auto-test.sh就是干这个的:
#!/bin/bash # scripts/auto-test.sh set -o pipefail mvn test > test-output.log 2>&1 STATUS=$? if [ $STATUS -ne 0 ]; then echo "测试失败,触发自动修复" claude -p "测试失败,请阅读 test-output.log,定位失败根因并修复实现代码。只改实现,不要改测试断言,除非断言本身写错了。" \ --allowed-tools Read,Grep,Glob,Write,Edit,Bash \ --max-turns 15 \ --output-format json else echo "测试通过" fi脚本逻辑很直白:跑测试,失败就把日志喂给 Claude,让它分析并修复。--allowed-tools里给了Edit和Bash,因为修复阶段需要改代码和重跑测试。
这里有个细节值得说:修复请求里明确写了"只改实现,不要改测试断言"。如果不加这句,Claude 有时会走捷径——直接把断言改成实际值,测试是绿了,但问题被掩盖了。这是我在实际项目里踩过的坑,护栏必须写进提示词。
整个循环的时序是这样的:
用户: 为 UserService 生成测试 ↓ Claude: 分析代码 → 生成 UserServiceTest.java ↓ Hook: 自动运行 mvn test ↓ 测试失败: 预期 "张三",实际 null ↓ Claude: 读日志 → 定位到 UserService 未处理空输入 → 修复 ↓ Hook: 再次运行 mvn test ↓ 测试通过 ↓ Claude: 建议重构,等待确认失败反馈环节,Claude 会区分三类问题:编译错误直接修语法;断言失败对比预期与实际,判断是实现错还是测试错;运行时异常检查依赖注入和配置。这个分类逻辑写在 Skill 的"失败处理策略"里,保证每次行为一致。
6. 验证请求与成功结果
配置完成后,用一个小例子验证整条流水线。假设有个Calculator类:
public class Calculator { public int divide(int a, int b) { return a / b; } }启动 Claude Code,触发 Skill:
> /skill tdd-automation > 为 Calculator.divide 生成测试并运行预期行为:Claude 生成CalculatorTest.java,覆盖正常除法、除数为零、负数边界三种情况。Hook 自动跑测试,divide(1, 0)会抛ArithmeticException,测试红。Claude 读日志后修复divide,加上除零判断,重跑测试,全绿。
验证成功的标志有三个:test-output.log最后一次记录是BUILD SUCCESS;CalculatorTest.java里有三个独立的@Test方法;Calculator.java的divide方法多了边界处理逻辑。
如果想在 CI 里验证,用 Headless 模式跑一遍:
claude -p "运行所有单元测试,如果有失败则分析根因并修复,最多修复三轮" \ --output-format json \ --max-turns 30 \ --allowed-tools Read,Grep,Glob,Write,Edit,Bash返回的 JSON 里result字段会描述最终状态,num_turns告诉你实际用了几轮。三轮内没修好就停下,避免无限循环烧 Token。
7. 本篇常见错排查
Hook 不触发:检查.claude/settings.json里matcher是否写成Write|Edit,正则要匹配工具名。另外确认auto-test.sh有执行权限,chmod +x scripts/auto-test.sh。
测试跑红但 Claude 不修复:多半是auto-test.sh里claude -p的 API 环境变量没继承。在脚本开头显式导出ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY,或者确认它们已在 shell 里 export。
Claude 改测试断言而不是改实现:提示词里必须加"只改实现,不要改测试断言"这类约束。如果已经发生,用git diff回滚测试文件,重新触发修复。
修复循环停不下来:--max-turns设小一点,比如 10 到 15。同时检查是不是测试本身写错了——比如断言了一个永远不可能的值,Claude 怎么改实现都过不了。这种情况要人工介入改测试。
权限被拒:permissions.allow里没放对应的 Bash 命令。比如用 Gradle 但白名单里只有mvn test,就会卡住。按项目实际测试命令补进去。
上下文丢失:长会话里 Claude 忘了 Skill 规范。用/skill tdd-automation重新加载,或者把关键约束写进CLAUDE.md,它会在每次会话自动读取。
排查时优先看test-output.log和 Claude 的流式输出,推理链条是可见的,能直接看出它卡在哪一步。
8. 语义一致 CTA 与落地建议
跑通本地循环后,下一步是把它接进 CI。GitHub Actions 里用 Secrets 注入密钥,调用 Headless 模式做 PR 审查:
- name: Claude TDD Check env: ANTHROPIC_BASE_URL: https://taotoken.net/api ANTHROPIC_API_KEY: ${{ secrets.TAOTOKEN_API_KEY }} run: | claude -p "运行测试,失败则修复,最多三轮" \ --output-format json \ --max-turns 30 \ --allowed-tools Read,Grep,Glob,Write,Edit,Bash密钥在仓库 Settings 的 Secrets 里配置,不要写进 YAML。接入文档和 API Key 管理都在下面两个入口:
接入文档: https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite API Keys: https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite如果你主要用 Claude Code 做长期编码和 Agent 任务,Coding Plan 的额度模型比按次调用更划算,适合把 TDD 循环常态化:
Coding Plan: https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite想先手动验证模型在测试生成上的表现,可以直接在模型对话里试:
模型对话: https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite落地节奏建议这样排:本周先在个人项目里跑通单模块循环,记录时间和 Token 消耗;本月把测试生成 Skill 固化进团队仓库,用 Git 管理版本;下个 Sprint 选一个中等复杂度模块,完整跑一遍生成到修复的闭环,用数据判断值不值得推广到全项目。护栏先立起来——白名单、max-turns、提示词约束,这三样缺一不可。