TL;DR

TDD 并未死亡,而是在 AI 时代进化为新的形态:

  1. TDD 的困境 — 红绿重构循环在 AI 生成代码时代显得低效
  2. AI-First 测试 — 从”先写测试”到”验证 AI 输出”
  3. 意图驱动验证 — 人类定义场景和边界,AI 生成测试矩阵
  4. 活的文档 — 测试即需求,需求即测试,AI 维持同步

关键洞察:测试的目的不是证明代码正确,而是建立信任。


📋 本文结构

  1. TDD 的辉煌与疲惫
  2. 为什么经典 TDD 在 AI 时代遇到挑战
  3. AI-First 测试范式
  4. 实战:从红绿重构到意图验证
  5. 测试即需求:活的文档
  6. 反直觉洞察:测试越多,开发越快
  7. 迁移路径:从 TDD 到 AI-First
  8. 结语:测试的终极目的

TDD 的辉煌与疲惫

让我们先向 TDD 致敬。

2000 年代初,Kent Beck 提出测试驱动开发(Test-Driven Development),这个简单的理念彻底改变了软件行业:

  1. :写一个失败的测试
  2. 绿:写最少代码让测试通过
  3. 重构:优化代码,保持测试通过

这个循环看似简单,却解决了软件开发的几个根本问题:

  • 设计压力:测试强迫你思考接口设计
  • 回归保护:修改代码时不会破坏已有功能
  • 信心建立:重构时心里有底
  • 文档效果:测试就是最好的使用文档

TDD 的黄金时代

在 2010 年代,TDD 成为”严肃”软件开发的标志:

  • 敏捷运动:TDD 是敏捷开发的核心实践
  • XP 极限编程:测试优先是基本原则
  • 开源项目:高质量项目必须有高测试覆盖率
  • 面试标准:”你会写单元测试吗?”成为必问题

但阴影也在蔓延

然而,随着时间的推移,一些问题开始浮现:

场景一:测试维护地狱

小李的项目有 80% 的测试覆盖率,这本该是好事。但当产品经理要求修改一个按钮文案时,他花了 2 小时修改了 47 个测试文件。

“我只是把 ‘提交’ 改成 ‘保存’,为什么 47 个测试要失败?”

场景二:脆弱测试

小王的团队坚持 TDD,但他们的测试非常脆弱:

  • 修改实现细节 → 测试失败
  • 重命名内部函数 → 测试失败
  • 调整日志格式 → 测试失败

测试成了变更的阻力,而非安全网。

场景三:AI 生成代码的冲击

当 Copilot/Cursor 可以一次性生成 100 行正确代码时,经典的 TDD 循环显得笨拙:

  1. AI 生成代码
  2. 人类:”等等,我先写个失败测试”
  3. 写测试
  4. 运行测试(失败)
  5. AI:”为什么不直接让我生成正确的代码?”

这就像有了自动铅笔,却还在用削笔刀。


为什么经典 TDD 在 AI 时代遇到挑战

挑战 1:测试编写速度 vs 代码生成速度

经典 TDD 假设:

  • 写测试很快
  • 写实现很慢
  • 所以先写测试能节省时间

AI 时代的现实:

  • 写测试:人工,需要思考场景和边界,较慢
  • 写实现:AI 生成,几秒钟完成,较快

速度对比逆转,导致 TDD 的时间投资回报率下降。

挑战 2:测试意图 vs 实现细节

经典 TDD 强调:

  • 测试应该关注行为,而非实现
  • 重构时测试应该继续通过

但现实是:

  • 很多测试实际上耦合了实现细节
  • AI 生成的代码可能与人类写的结构不同
  • 测试变得脆弱

挑战 3:测试覆盖率幻觉

经典观念:高覆盖率 = 高质量

AI 时代的问题:

  • AI 可以轻松生成覆盖所有分支的测试
  • 但这些测试可能没测到真正重要的场景
  • 覆盖率 100% 但 bug 依然存在

质量 != 覆盖率,这个道理在 AI 时代更加凸显。

挑战 4:测试即文档的失效

TDD 的承诺:测试就是最好的文档

现实:

  • 测试代码往往难以阅读
  • 测试关注边界情况,而非典型用法
  • 新手从测试中学到的有限

AI-First 测试范式

核心转变

“人类写测试验证代码”“人类定义意图,AI 生成验证”

维度 经典 TDD AI-First 测试
起点 写失败测试 定义意图和场景
测试生成 人工编写 AI 生成测试矩阵
验证重点 代码行为 意图实现
维护方式 人工更新 AI 辅助同步
文档形式 测试代码 自然语言 + 测试

AI-First 测试三层模型

意图层 (Intent Layer):
  描述: 人类用自然语言定义期望的行为
  示例: "当用户未登录时,访问 /dashboard 应该重定向到 /login"
  维护者: 人类 (产品/开发)
  
场景层 (Scenario Layer):
  描述: AI 将意图扩展为具体的测试场景
  示例: 
    - 场景1: 完全未登录用户
    - 场景2: 登录但 token 过期
    - 场景3: 登录但权限不足
  生成者: AI (基于意图和领域知识)
  
验证层 (Verification Layer):
  描述: AI 生成具体的测试代码
  示例: 具体的测试函数、mock、断言
  生成者: AI (基于场景)
  审查者: 人类 (关键场景)

新模式:意图 → 场景 → 验证

人类: "用户下单后应该收到确认邮件"
    ↓
AI 场景生成:
  - 正常下单流程
  - 下单但支付失败
  - 下单但库存不足
  - 下单但邮件服务不可用
  - 并发下单场景
    ↓
AI 测试生成:
  - 为每个场景生成测试代码
  - 建议需要的 mock
  - 识别边界条件
    ↓
人类审查:
  - "等等,并发场景很重要,但测试不够全面"
  - AI 补充并发测试
    ↓
测试运行 + 代码生成

实战:从红绿重构到意图验证

场景:用户注册功能

经典 TDD 方式

// 1. 红:写失败测试
test('should create user with valid data', () => {
  const user = createUser({ email: 'test@example.com', password: '123456' });
  expect(user.email).toBe('test@example.com');
  expect(user.id).toBeDefined();
});

// 2. 绿:写最少代码
function createUser(data) {
  return { id: 1, email: data.email };
}

// 3. 重构:优化实现
// ... 实际的数据库操作

AI-First 方式

## 意图定义

### 功能意图
用户可以通过提供邮箱和密码注册账号。

### 业务规则
- 邮箱必须符合格式
- 密码必须至少 6 位
- 邮箱不能已被注册
- 注册成功后发送验证邮件
- 密码必须加密存储

### 边界条件
- 无效邮箱格式
- 密码太短
- 重复注册
- 邮件服务不可用
// AI 生成的测试矩阵
// 由意图自动扩展的场景

describe('User Registration', () => {
  // 主场景
  test('creates user with valid email and password', () => {});
  
  // 边界条件 (AI 自动识别)
  test('rejects invalid email format', () => {});
  test('rejects password shorter than 6 characters', () => {});
  test('rejects duplicate email registration', () => {});
  test('handles email service failure gracefully', () => {});
  
  // 安全场景 (AI 从意图推导)
  test('stores password in encrypted form', () => {});
  test('prevents SQL injection in email field', () => {});
  
  // 性能场景
  test('completes registration within 500ms', () => {});
  test('handles concurrent registrations correctly', () => {});
});

关键差异

方面 经典 TDD AI-First
思考重点 如何写测试 意图是否完整
场景覆盖 依赖人工思考 AI 系统性扩展
边界条件 容易遗漏 AI 自动补充
维护成本 变更时手动更新 AI 辅助同步

测试即需求:活的文档

经典问题:测试与需求不同步

传统开发中:

  1. 产品经理写需求文档
  2. 开发写代码
  3. 测试写测试用例

三个文档,三个版本,很快就不同步。

AI-First 解决方案:单一真相源

单一真相源: 意图定义文件 (user-registration.intent.yml)

内容:
  功能: 用户注册
  触发条件: 用户提交注册表单
  期望结果: 创建新用户账号
  
  业务规则:
    - 邮箱格式验证
    - 密码强度要求
    - 唯一性检查
    - 邮件通知
  
  边界条件:
    - 无效输入
    - 重复注册
    - 服务不可用

下游产物 (AI 自动生成):
  - 测试代码
  - API 文档
  - 用户故事
  - 验收标准

当意图变更时,AI 自动更新所有下游产物。

活的文档系统

意图定义 (人类维护)
    ↓
AI 同步引擎
    ↓
├── 测试代码 (自动更新)
├── API 文档 (自动更新)
├── 用户手册 (自动更新)
└── 验收清单 (自动更新)

价值:需求、测试、文档永远同步。


反直觉洞察:测试越多,开发越快

洞察 1:AI 生成测试不是偷懒,是杠杆

担忧:让 AI 生成测试,工程师会变懒。

现实:

  • AI 生成的是”验证代码”
  • 人类需要思考的是”验证什么”
  • 后者才是高价值工作

就像计算器没有让数学家变懒,而是让他们思考更难的问题。

洞察 2:测试代码应该被生成,而非手写

反直觉:测试代码是高度模式化的,正好适合 AI 生成。

人类应该专注于:

  • 定义意图
  • 识别边界条件
  • 设计场景

AI 负责:

  • 编写具体的断言
  • 设置 mock
  • 处理重复结构

洞察 3:覆盖率应该由 AI 保证,人类保证意图

传统:人类努力达到高覆盖率。

AI-First:

  • AI 确保技术层面的覆盖率
  • 人类确保业务层面的覆盖度
  • 两者结合才是真正的质量保证

迁移路径:从 TDD 到 AI-First

阶段 1:意图显式化 (1-2 周)

目标:开始用自然语言描述测试意图

实践

// 之前
test('createUser works', () => { ... });

// 之后
test('Intent: User can register with valid email and password', () => {
  // AI 可以基于这个意图生成具体测试
  ...
});

阶段 2:AI 辅助生成 (2-4 周)

目标:用 AI 生成测试模板和边界条件

工具

  • GitHub Copilot 生成测试代码
  • ChatGPT/Claude 扩展测试场景

实践

人类: "测试用户登录功能"
AI: "我将生成以下测试场景:..."
人类: "加上并发登录的场景"
AI: "已添加"

阶段 3:意图驱动开发 (1-2 个月)

目标:意图成为主要工件,测试代码自动衍生

系统

# 项目结构
features/
  user-registration/
    intent.yml          # 人类维护
    scenarios.auto.js   # AI 生成
    tests.auto.js       # AI 生成
    manual-tests.js     # 人类补充 (复杂场景)

阶段 4:全自动化 (3-6 个月)

目标:意图变更自动触发测试更新

CI/CD 集成

# CI 流程
1. 检测意图文件变更
2. AI 重新生成测试代码
3. 运行测试
4. 人类审查变更
5. 合并

结语:测试的终极目的

让我们回到根本问题:为什么要测试?

不是为了覆盖率数字。 不是为了满足流程要求。 不是为了写报告。

测试的终极目的是建立信任——

  • 信任代码在修改后依然正确
  • 信任重构不会破坏功能
  • 信任新功能不会引入回归
  • 信任团队可以持续交付

AI-First 测试并没有改变这个目的,它只是让我们更高效地达到这个目的。

TDD 没有死,它进化了。

从”测试驱动开发”到”意图驱动验证”,我们实际上是在做同一件事:用清晰的思维指导代码

只是现在,我们有了一个强大的搭档。


系列关联阅读

下一篇预告:#49 Context 工程:AI-Native 开发的核心能力


AI-Native软件工程系列 #47

Published on 2026-03-13