TDD:测试驱动开发,分为三个阶段,红灯阶段,绿灯阶段,重构阶段。
几年前第一次接触 TDD 。当时在一个项目里被强制要求"先写测试 再写代码"。坚持了三个月,最后整个团队默契地放弃——因为太慢了。同样一个功能,先写测试要多花 50%-80% 的时间,老板看不到产出,我们自己也累。
后来我基本没再认真做过 TDD。偶尔有人提我都翻白眼:"那玩意儿在真实业务里跑不起来。"
直到 2025 年下半年开始重度用 Claude / Cursor 做嵌入式开发,我意外地把 TDD 用回来了——而且这次是真心觉得好用。今天写一下为什么。
预警:本文不是给你布道 TDD。是讲当 AI 进入工作流后,TDD 的成本结构发生了根本变化,原来不划算的事现在划算了。如果你跟我十年前一样讨厌 TDD,强烈建议看完。
一、传统 TDD 为什么大多数人坚持不下来
经典 TDD 流程:
1. 写一个失败的测试(Red)
2. 写最少代码让测试通过(Green)
3. 重构(Refactor)
4. 回到 1
理论上完美——实际三个痛点:
痛点 1:写测试比写功能还累
要测试一个 parseConfig(yaml) 函数,你得:
- 想清楚 N 种输入(合法 / 非法 / 边界 / 极端)给每种输入构造 fixture写 assertion跑通
往往测试代码 200 行,功能代码 50 行。开发团队心理上接受不了。
痛点 2:需求变化让测试变成债务
需求改了,功能代码改 5 行,测试代码要改 50 行——不然全红。很多团队最后选择"先改功能、测试坏了删掉"。删着删着 TDD 就名存实亡。
痛点 3:很多代码"难测试"
UI、硬件交互、第三方 API、复杂时序——这些代码写测试比写功能难 10 倍。嵌入式领域尤其严重,一个驱动函数依赖寄存器 / 时钟 / 中断,纯软件 mock 几乎不可能。
痛点 4:测试质量难保证
测试写得烂的话——比如只测 happy path、断言空泛——还不如不写。给团队"我们有测试"的虚假安全感。而写好测试需要的技能跟写好功能一样难。
这四个痛点叠加,使 TDD 在实际项目里理论上正确、实际上跑不起来。
二、AI 改变了什么
四个痛点里,AI 直接打掉三个。
痛点 1 → AI 让"写测试"变得几乎免费
让 Claude 给一个函数生成一组完整测试用例——30 秒。覆盖度比你手写还高(AI 见过更多 edge case)。
写测试的成本从"高于功能"变成"低于功能"。TDD 的最大经济障碍消失。
痛点 2 → AI 让"维护测试"变得便宜
需求变了,原来要手改 50 行测试。现在 prompt:
功能签名从 X 变成 Y,请同步更新这组测试。
AI 30 秒搞定。我手动 review 5 分钟。测试维护成本降一个数量级。
痛点 3 → AI 让"难测代码"变得能测
嵌入式驱动测试难在"mock 硬件"。让 AI 帮你写:
基于这份 datasheet 的 register map,给我一个 mock 寄存器层,能模拟以下行为:
- 写入 0x01 到 CTRL → STATUS 在 100us 后变成 0x80
- 读 DATA 寄存器返回上次设的 value
- ...
AI 给的 mock 层 200-500 行,你手写要 1-2 天。
痛点 4 → AI 让"测试质量"有保障
让 AI review 你的测试:
这组测试覆盖了哪些场景?哪些边界 case 缺失?
- 空输入
- 极大值
- 并发
- 错误注入
- 类型异常
AI 列出来你查漏补缺。测试质量从依赖个人经验变成有兜底。
三、AI-TDD 工作流(我的实操版本)
放弃经典三步循环,我现在的工作流是这样:
Phase 1: 跟 AI 对齐意图(5-10 min)
↓
Phase 2: AI 生成测试套件(5-10 min)
↓
Phase 3: 人工 review 测试(10-15 min) ← 关键
↓
Phase 4: AI 实现功能(5-15 min)
↓
Phase 5: 跑测试 → 红 → 让 AI 修
↓
Phase 6: 全绿后做边界扩展
下面分阶段讲。
四、Phase 1:跟 AI 对齐意图
不要直接说"写一个 parseConfig 函数"——那是 1990 年代的需求传达方式。
正确做法是写 spec:
我要实现一个 YAML config parser。
输入:YAML 字符串
输出:Config 结构体 / 错误
【字段】
- name: string, required, 1-64 字符, 只能字母数字 _ -
- port: int, required, 1-65535
- timeout_ms: int, optional, default 5000, 范围 100-60000
- backends: list of {host, port}, required, 至少 1 个
- tls: nested object, optional
- cert_path: string, 文件必须存在
- key_path: string, 文件必须存在
- ca_path: string, optional
【错误处理】
- YAML 语法错误 → ParseError,含行列号
- 字段缺失 → ValidationError("missing field: <name>")
- 字段类型错 → ValidationError("invalid type for <name>: expected X got Y")
- 范围 / 格式错 → ValidationError("invalid value for <name>: ...")
- 文件不存在 → ValidationError("file not found: <path>")
【约束】
- 不允许未知字段(strict mode)
- 数字字段不接受字符串
- 路径用 forward slash 统一
这份 spec 有 30 行——以前我直接写代码可能 3 行需求注释。但这 30 行就是后面所有测试和代码的契约。
写 spec 比写代码难——但这件事 AI 也能帮你:
我要做个 YAML config parser,主要字段是 name / port / backends。
请帮我列出一份完整 spec:
- 该有哪些字段
- 每个字段的约束(类型 / 范围 / 格式 / required)
- 错误处理策略
- 边界情况
让我能基于这份 spec 写测试。
AI 给完,你 review 增删——人 + AI 的对齐过程,本身就是需求澄清。
五、Phase 2:AI 生成测试套件
把 spec 给 AI:
基于这份 spec:
<贴 spec>
写一个完整的测试套件。要求:
1. 用 pytest
2. 每个字段的 happy path + 至少 3 个 error case
3. 嵌套 / 组合场景至少 5 个
4. 边界值(min / max / max+1 / min-1)
5. 类型错误(每个字段给一个错误类型)
6. 一组真实业务场景的 fixture(YAML 字符串放 fixtures/ 目录下)
7. 测试命名清晰:test_<scenario>_<expected>
8. 失败信息要包含 "实际值 vs 期望值"
9. 用 parametrize 减少重复
AI 一般给 30-80 个测试,800-2000 行。
我做过的真实项目里,spec 30 行 → 测试 1500 行。手写要 2 天,AI 给完 + review 半天。
六、Phase 3:人工 review 测试(最关键)
这一步不能跳。AI 生成的测试质量参差,必须人工把关。
review 的几个重点:
6.1 断言方向是否对
AI 经常会写:
assert result.port == 8080 # 实际期望是 8081
或者更隐蔽的:
assert err is not None # 应该是 isinstance(err, ValidationError)
断言写错 = 测试在保护错误的行为。很危险。
6.2 测试是否真的会失败
让 AI 故意把功能代码写错一行,跑测试——应该红的测试有没有红?
很多 AI 写的测试 mock 太多,导致功能代码怎么写都过。这种"绿色"测试比没有还糟。
6.3 边界 case 是否真的边界
AI 会写:
def test_port_max():
assert parse({"port": 65535}).port == 65535
def test_port_max_plus_1():
with pytest.raises(ValidationError):
parse({"port": 65536})
OK。但它会漏 0、负数、浮点数、字符串、None——这些都得加。
6.4 测试代码自己有没有 bug
AI 写的 fixture 经常有 typo / 不一致。例如:
yaml_data = """
name: test
port: 8080
backens: # ← typo: backends
- host: localhost
"""
这种 fixture 让测试结果完全错乱——它在测"backends 缺失",但你以为它在测"backends 配置正常"。
review 测试的时间投入大约是 AI 生成时间的 2-3 倍——这是 AI-TDD 里最贵的环节,不能省。
七、Phase 4:AI 实现功能
测试 review 通过后,让 AI 写实现:
这是测试套件:
<贴测试代码>
请实现 parse_config 函数让所有测试通过。
要求:
1. 不要在测试上动手脚(不许改测试让自己过)
2. 如果某个测试设计本身有问题,先告诉我,我决定要不要改
3. 实现风格:纯函数 / 不依赖全局状态
4. 用 ruamel.yaml 解析(保留 yaml 1.2 语义)
5. 错误信息要 actionable
关键 prompt:"不要在测试上动手脚"——AI 真的会改测试让自己过。我有过几次它默默把 assert x == 5 改成 assert x == 4,因为它实现里返回了 4。
跑测试,看红的有几个。
八、Phase 5:让 AI 修
这些测试红了:
<贴 pytest output>
请:
1. 分析每个失败原因
2. 修改实现(不是测试)
3. 如果你发现某测试期望可能写错了,单独标出来给我决定
通常 1-3 轮就能全绿。
我做过的项目里,AI 一次实现就全绿的概率约 40%。1 轮修复后绿的约 80%。3 轮还修不好的约 5%——这种通常是 spec 本身有歧义,要回 Phase 1 重新对齐。
九、Phase 6:边界扩展
全绿了不是结束。让 AI 帮你找还没测的角落:
基于以下功能代码 + 测试:
<贴代码 + 测试>
请:
1. 找出尚未覆盖的 code path(用 coverage 思路分析)
2. 找出可能的 race condition / 并发问题
3. 找出依赖外部状态(文件 / 环境变量 / 时间)的部分,建议补充对应 mock 测试
4. 找出错误处理路径中没被触发的分支
5. 给出补充测试代码
AI 经常找出 5-15 个补充测试。我加上去再跑——通常会发现 1-3 个真 bug。
pytest --cov=parse_config --cov-report=term-missing
跑 coverage 看实际覆盖。覆盖到 95%+ 才算 done。
十、跟传统 TDD 的对比(真实数字)
我对一个中等复杂度的功能(约 300 行实现)做过三种方式对比:
| 方式 | 实现耗时 | 测试覆盖率 | 上线后 bug |
|---|---|---|---|
| 不写测试,直接撸代码 | 4h | 0% | 上线第一周 5 个 bug |
| 传统 TDD(手写) | 14h | 65% | 上线第一周 2 个 bug |
| AI-TDD | 6h | 92% | 上线第一周 0 个 bug |
AI-TDD 比"直接撸"只多 50% 时间,但 bug 数从 5 → 0。比传统 TDD 时间少 60%、覆盖率高 27%。
AI-TDD 是目前性价比最高的开发方式。
十一、什么场景特别适合 AI-TDD
1. 纯函数 / 数据处理
输入 → 输出 没副作用的函数。AI 最强场景。
2. parser / serializer / validator
边界 case 多但模式清晰。AI 生成测试覆盖度极高。
3. 状态机
每个 state transition 都该有测试。AI 能枚举完整 transition。
4. API contract
接口的 happy path + error path + auth path。AI 套路化生成。
5. 算法实现
排序 / 搜索 / 数学函数。AI 生成的测试比 leetcode 题库还全。
6. 嵌入式驱动(带 mock 寄存器层)
参考之前写的硬件自动化测试。AI 帮你 mock 硬件后,驱动也能 TDD。
十二、什么场景不适合
1. UI 交互逻辑
AI 测试可以做单元,但用户体验类的东西测试无意义。靠人工 + E2E。
2. 性能优化代码
性能 bug 不在功能正确性。性能测试要专门的 benchmark,不是 TDD。
3. 探索性原型
你都不知道最终要什么——写 spec 写不出来。先撸代码再补测试。
4. 一次性脚本
跑完就扔,TDD 投入回不来。
5. 跟物理世界强耦合
机器人控制、模拟仿真——mock 失真度太大,测过不代表实际能跑。
6. AI / ML 模型本身
模型行为是统计性的,不是确定性的。"测试"要换成"评估指标"——是另一套体系。
十三、经验
1. spec 阶段花的时间不是浪费
很多人觉得"写 30 行 spec 还不如写代码"——但 spec 是后面所有 AI 工作的种子。spec 越精确,AI 后面越准。
我现在写 spec 平均花 15-20 分钟。这是整个流程里"投入产出比最高"的 15 分钟。
2. 不要让 AI 同时写测试和实现
我试过"一次给 spec,让 AI 同时给测试 + 实现"——结果AI 会让自己的测试测自己的实现,循环自洽但实际错的。
必须先生成测试 → 人 review → 再生成实现。两步分开,有人工把关在中间。
3. test-only commit + impl commit 分开
git 历史里把"加测试"和"加实现"做成两个 commit。这样 review 容易:
- commit 1: "Add tests for parse_config" — reviewer 关注 spec / 测试设计commit 2: "Implement parse_config" — reviewer 关注实现细节
也方便后续:如果实现改了,可以 revert impl commit 但保留测试。
4. 测试 + 实现都用 AI 生成不会让你失业
很多人怕"AI 都能写测试 + 实现了,要工程师干嘛"。
真相:AI 接管的是"机械劳动",留给你的是"判断"——
- spec 写得对不对测试设计合不合理边界 case 漏没漏实现风格符不符合团队维护成本可不可控
这些判断全是经验性工作——新人做不来。AI 时代的高级工程师价值更高,不是更低。
5. CI 必须跑 100% 测试 + coverage 阈值
AI-TDD 的代码量大、测试量更大——必须 CI 强制跑:
test:
script:
- pytest --cov=src --cov-fail-under=85
低于 85% 覆盖直接 fail。这是防止"AI 写的代码 + AI 写的测试 + 没人看 = 灾难"的最后一道防线。
6. 留一类"人工写的整合测试"
单元测试 AI 写。但端到端 / 集成测试坚持人工写——这种测试反映"真实业务流",AI 不知道你们公司的真实业务长什么样。
规则:单元测试 AI 写 90%,集成测试人工写 90%。
十四、AI-TDD 的盲区和风险
1. AI 测试的"思维定式"
AI 见过的测试模式有限。某些罕见 case(如"内存对齐边界 + 大端字节序 + 跨页访问")AI 不会想到。
对策:补充测试时多用"如果是攻击者,他会怎么打"的视角,AI 给不出,你自己想。
2. 测试 + 实现一起飘
AI 看测试写实现 → 发现某测试难过 → 偷偷改测试 → 你没注意 → 测试和实现一起偏离 spec。
对策:每次让 AI 改代码后,diff 看测试文件有没有变。变了要警惕。
3. 过度测试
AI 倾向"写更多测试"——一个简单函数给你 100 个测试。真的有用的可能 30 个。剩下 70 个增加维护成本但不增加保障。
对策:review 时主动删除"测同一行为不同写法"的重复测试。
4. spec 漂移
迭代过程中需求变了,spec 没同步更新。AI 接着按老 spec 改 → 一致性破坏。
对策:spec 文档跟代码放一起(同 repo),改需求 = 改 spec + 改测试 + 改实现的 atomic commit。
5. 安全敏感代码不能完全信 AI
加密 / 鉴权 / 输入校验 / 反序列化——AI 写的实现可能"测试都过但有 CVE"。
对策:这类代码强制人工 review 实现 + 加 fuzz testing 不只是单元测试。
十五、ROI 排序:哪些场景立刻能上 AI-TDD
立刻就上(一天见效)
新写的纯函数:parser / validator / formatter / converter
现有函数的 bug fix:先写复现 bug 的测试,再让 AI 修
一周内试一次
新模块的 API 层:路由 + handler + validator
现有模块的重构:先用 AI 补一波测试,再放心重构
一个月内推广
全团队 spec-first 流程:所有 PR 必带 spec + 测试
CI 集成 coverage gate
谨慎对待
遗留代码补测试:先补集成测试,单元测试可能没意义
跨服务集成:mock 不到位时 TDD 是假的
十六、写在最后
AI 把 TDD 里"贵的部分"变便宜了:
- 写测试 → 免费维护测试 → 免费mock 难测代码 → 免费找漏掉的 case → 免费
留给人做的是判断:spec 对不对、测试设计合不合理、bug 是不是真 bug。这部分本来就是工程师该做的事。
如果你跟十年前的我一样觉得 TDD 是大忽悠——今天找一个新写的纯函数,按本文 6 步走一遍。一个小时之内你会改主意。
如果你已经在用 AI 写代码但还没用 AI-TDD——你少拿了一半 AI 的红利。AI 写完代码不写测试 = 你在生产环境裸奔。
最后一个观点:未来 2-3 年,"会做 AI-TDD" 会跟"会用 git"一样,是行业基础技能。早点上手,早点习惯。
128