TDD × SDD × AI Coding:从"测试驱动"到"规范驱动"的智能协作实践
一、引言:AI Coding 时代的"驱动方式"之争
AI 辅助编程工具(GitHub Copilot、Cursor、WorkBuddy 等)正在快速改变软件开发的范式。但随之而来的一个核心问题浮出水面:
AI 写代码,谁来保证代码是对的?
传统工程界已有两个成熟答案:
- TDD(Test-Driven Development,测试驱动开发):先写测试,再写实现,让测试成为代码正确性的"守门员"。
- SDD(Specification-Driven Development,规范驱动开发):先写规范(需求文档、接口约定、类型定义),再驱动 AI 生成代码,让规范成为 AI 的"航线图"。
两者并非对立,而是互补。本文将从理念对比、实践流程、工具链搭建到完整案例,带你深入理解如何在 AI Coding 时代将 TDD 与 SDD 融合,构建可信、可维护、可扩展的 AI 辅助研发体系。
二、概念澄清:TDD vs SDD 是什么?
2.1 TDD(测试驱动开发)
TDD 是 Kent Beck 于 1990 年代提出的极限编程实践,核心循环为"红-绿-重构":
Red → 写一个失败的测试
Green → 写最少的代码让测试通过
Refactor → 在绿灯下优化代码质量
核心价值:
- 测试即文档,即规格
- 防止过度设计
- 持续保障代码可回归
在 AI Coding 中的局限:
- AI 不会自发写测试,需要人工驱动
- 测试粒度不统一,AI 生成测试易流于形式(只测 happy path)
- 缺乏全局视角,难以在架构层约束 AI 输出
2.2 SDD(规范驱动开发)
SDD 是 AI Coding 时代兴起的方法论,核心思路是:用结构化、机器可读的规范文档作为 AI 的输入约束,让 AI 在规范边界内生成代码。
规范的形式包括但不限于:
| 规范类型 | 示例 |
|---|---|
| 接口规范 | OpenAPI / gRPC Proto |
| 类型规范 | TypeScript Interface / JSON Schema |
| 行为规范 | Gherkin / BDD 场景文本 |
| 架构规范 | ADR(架构决策记录) |
| 代码规范 | ESLint Rules / Checkstyle |
| AI 任务规范 | SKILL.md / .cursorrules |
核心价值:
- 约束 AI 输出范围,提升生成准确率
- 规范即上下文,减少 AI “幻觉”
- 跨会话保持一致性
在 AI Coding 中的局限:
- 规范写得不好,AI 生成的代码同样错误
- 规范更新滞后,"规范漂移"导致 AI 代码与实际不符
- 缺少运行时验证,规范停留在"纸面"
2.3 核心对比
| 维度 | TDD | SDD |
|---|---|---|
| 核心产物 | 测试用例 | 规范文档 |
| 约束时机 | 运行时(测试执行) | 编码前(上下文输入) |
| AI 协作方式 | AI 实现满足测试的代码 | AI 根据规范生成代码 |
| 质量保障层 | 功能正确性 | 结构一致性 |
| 适用场景 | 业务逻辑、算法层 | 接口层、架构层 |
| 维护成本 | 测试维护成本高 | 规范维护成本高 |
| AI 友好度 | 中(需精准描述测试意图) | 高(结构化文本天然适合 AI 解析) |
三、整体架构:TDD + SDD 融合的 AI Coding 体系
四、实践流程:五步融合法
Step 1:需求 → 规范(SDD 起点)
在拿到需求后,不要急于让 AI 写代码,先将需求转化为结构化规范:
实操示例: 以"用户登录接口"为例
# openapi.yaml(规范先行)
paths:
/auth/login:
post:
summary: 用户登录
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [username, password]
properties:
username:
type: string
minLength: 3
maxLength: 50
password:
type: string
minLength: 8
responses:
'200':
description: 登录成功
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
'401':
description: 用户名或密码错误
Step 2:规范 → 测试骨架(TDD 前置)
规范写完后,让 AI 根据规范生成测试骨架(而非直接生成实现代码):
AI 提示词模板:
根据以下 OpenAPI 规范,生成完整的 Jest 单元测试骨架。
要求:
1. 覆盖所有 HTTP 状态码场景
2. 包含边界值测试(minLength/maxLength)
3. 使用 describe/it 结构,测试描述用中文
4. 先只写测试,不写实现
[粘贴 OpenAPI 规范]
生成的测试骨架示例:
// auth.test.ts
describe('用户登录接口 POST /auth/login', () => {
describe('正常登录', () => {
it('正确账号密码应返回 200 和 token', async () => {
// TODO: 待实现
expect(true).toBe(false); // Red 状态
});
});
describe('参数校验', () => {
it('用户名少于3位应返回 400', async () => {
expect(true).toBe(false);
});
it('密码少于8位应返回 400', async () => {
expect(true).toBe(false);
});
it('缺少 username 字段应返回 400', async () => {
expect(true).toBe(false);
});
});
describe('认证失败', () => {
it('错误密码应返回 401', async () => {
expect(true).toBe(false);
});
it('不存在的用户应返回 401', async () => {
expect(true).toBe(false);
});
});
});
Step 3:AI 实现代码(受双重约束)
此时 AI 的实现代码同时受到规范约束(SDD)和测试约束(TDD)的双重限制:
AI 提示词模板:
现在请实现以下接口,要求:
1. 严格遵循 openapi.yaml 中的接口规范
2. 实现必须让 auth.test.ts 中所有测试通过
3. 使用 Spring Boot 3 + Java 17
4. 包含参数校验(使用 Bean Validation)
5. 不要超出规范范围添加额外字段
规范文件:[openapi.yaml]
测试文件:[auth.test.ts]
Step 4:重构与规范更新(双向反馈)
代码通过测试后,进入重构阶段,同时维护规范的准确性:
Step 5:CI 流水线中的规范守护
将 SDD 和 TDD 的质量门控集成到 CI/CD 流水线:
五、完整实践案例:云安全告警管理模块
以一个真实场景为例:云安全平台的告警规则管理接口,演示完整的 TDD+SDD+AI 协作流程。
5.1 项目结构
alert-rule-service/
├── specs/ # SDD 规范层
│ ├── openapi.yaml # 接口规范
│ ├── alert-rule.schema.json # 数据模型规范
│ └── ADR-001-alert-engine.md # 架构决策记录
├── src/
│ ├── main/java/.../
│ │ ├── controller/ # AI 根据规范生成
│ │ ├── service/ # AI + TDD 驱动实现
│ │ └── model/ # 从 schema 生成
│ └── test/java/.../ # TDD 测试用例
├── .workbuddy/
│ └── skills/
│ └── alert-rule-dev/ # 项目级 SKILL
│ └── SKILL.md # AI 任务规范
└── .spectral.yaml # 规范 Lint 配置
5.2 SKILL.md:AI 任务规范示例
# SKILL: 告警规则模块开发规范
## 技术栈约束
- Spring Boot 3.2 + Java 17
- MyBatis-Plus ORM
- Jakarta Bean Validation
- JUnit 5 + Mockito
## 接口规范
- 严格遵循 specs/openapi.yaml
- 所有接口返回统一格式:{ code, message, data }
- 分页接口使用 PageResult<T> 包装
## 命名规范
- Controller 方法名与 operationId 一致
- 异常使用 BusinessException(ErrorCode.XXX) 抛出
- 日志使用 @Slf4j,禁止 System.out.println
## 测试要求
- 每个 Service 方法覆盖率 >= 85%
- Controller 层必须有集成测试
- 涉及数据库操作使用 @Transactional + H2 内存库
5.3 告警规则 CRUD 的测试先行示例
// AlertRuleServiceTest.java(测试骨架,Red 阶段)
@SpringBootTest
@Transactional
class AlertRuleServiceTest {
@Autowired
AlertRuleService alertRuleService;
@Test
@DisplayName("创建告警规则 - 正常参数应成功持久化")
void createAlertRule_withValidParams_shouldPersist() {
// Given
CreateAlertRuleRequest request = CreateAlertRuleRequest.builder()
.name("CPU使用率告警")
.metric("cpu_usage")
.threshold(85.0)
.severity(Severity.HIGH)
.build();
// When
AlertRuleVO result = alertRuleService.createAlertRule(request);
// Then
assertThat(result.getId()).isNotNull();
assertThat(result.getName()).isEqualTo("CPU使用率告警");
assertThat(result.getStatus()).isEqualTo(RuleStatus.ENABLED);
}
@Test
@DisplayName("创建告警规则 - 阈值超出范围(>100)应抛出业务异常")
void createAlertRule_withThresholdOver100_shouldThrowBusinessException() {
CreateAlertRuleRequest request = CreateAlertRuleRequest.builder()
.name("非法规则")
.metric("cpu_usage")
.threshold(150.0) // 非法值
.severity(Severity.LOW)
.build();
assertThatThrownBy(() -> alertRuleService.createAlertRule(request))
.isInstanceOf(BusinessException.class)
.hasMessageContaining(ErrorCode.INVALID_THRESHOLD.getMessage());
}
@Test
@DisplayName("查询告警规则列表 - 支持按严重等级过滤")
void listAlertRules_filterBySeverity_shouldReturnMatchedRules() {
// Given - 预置不同等级的规则
createTestRule("高危规则1", Severity.HIGH);
createTestRule("高危规则2", Severity.HIGH);
createTestRule("低危规则", Severity.LOW);
// When
PageResult<AlertRuleVO> result = alertRuleService.listAlertRules(
AlertRuleQuery.builder().severity(Severity.HIGH).pageSize(10).build()
);
// Then
assertThat(result.getTotal()).isEqualTo(2);
assertThat(result.getItems())
.allMatch(rule -> rule.getSeverity() == Severity.HIGH);
}
}
5.4 AI 驱动实现(Green 阶段)
AI 收到测试文件 + SKILL.md + OpenAPI 规范后,生成的 Service 实现:
// AlertRuleServiceImpl.java(AI 生成,受双重约束)
@Slf4j
@Service
@RequiredArgsConstructor
public class AlertRuleServiceImpl implements AlertRuleService {
private final AlertRuleMapper alertRuleMapper;
private final AlertRuleConverter converter;
@Override
@Transactional
public AlertRuleVO createAlertRule(CreateAlertRuleRequest request) {
// 参数校验(规范约束)
if (request.getThreshold() < 0 || request.getThreshold() > 100) {
throw new BusinessException(ErrorCode.INVALID_THRESHOLD);
}
// 重名检查
if (alertRuleMapper.existsByName(request.getName())) {
throw new BusinessException(ErrorCode.ALERT_RULE_NAME_DUPLICATE);
}
AlertRule entity = converter.toEntity(request);
entity.setStatus(RuleStatus.ENABLED);
entity.setCreateTime(LocalDateTime.now());
alertRuleMapper.insert(entity);
log.info("告警规则创建成功: id={}, name={}", entity.getId(), entity.getName());
return converter.toVO(entity);
}
@Override
public PageResult<AlertRuleVO> listAlertRules(AlertRuleQuery query) {
Page<AlertRule> page = new Page<>(query.getPageNum(), query.getPageSize());
LambdaQueryWrapper<AlertRule> wrapper = Wrappers.lambdaQuery(AlertRule.class)
.eq(query.getSeverity() != null, AlertRule::getSeverity, query.getSeverity())
.eq(query.getStatus() != null, AlertRule::getStatus, query.getStatus())
.orderByDesc(AlertRule::getCreateTime);
alertRuleMapper.selectPage(page, wrapper);
return PageResult.of(page, converter::toVO);
}
}
六、进阶:AI Coding 中的规范漂移治理
随着项目演进,规范与实现之间会出现"漂移",这是 SDD 面临的最大挑战:
治理方案:三道防线
七、工具链推荐
| 场景 | 推荐工具 | 说明 |
|---|---|---|
| 规范编写 | Stoplight Studio / Swagger Editor | 可视化 OpenAPI 编辑 |
| 规范 Lint | Spectral | 自定义规范校验规则 |
| 契约测试 | Pact.io | Consumer-Driven Contract |
| AI 编码 | WorkBuddy / Cursor | 结合 SKILL.md 使用 |
| 测试框架 | JUnit 5 + AssertJ(Java)/ Jest(TS) | 可读性强 |
| 覆盖率 | JaCoCo / Istanbul | CI 集成门槛检查 |
| 规范版本管理 | Git + Conventional Commits | 规范变更可追溯 |
| 接口 Mock | WireMock / MSW | 基于规范自动 Mock |
| 代码生成 | openapi-generator / swagger-codegen | 规范生成代码骨架 |
八、常见误区与解答
❌ 误区 1:有了 AI,不需要写测试了
实际情况: AI 生成的代码不能自证正确,测试是唯一客观标准。没有测试的 AI 代码库,熵增速度是人工代码库的 3 倍。
误区 2:规范文档太重了,敏捷开发不需要
实际情况: SDD 的规范不是传统的 Word 文档,而是机器可读的 YAML/JSON/TS 文件。写好一份 OpenAPI,AI 可以反复生成:测试、Mock、文档、代码——这是杠杆效应。
误区 3:TDD 和 SDD 选一个就够了
实际情况: SDD 管"写什么",TDD 管"对不对"。两者是正交关系,缺一不可。规范告诉 AI 边界,测试告诉 AI 预期。
误区 4:AI 会自动保持规范更新
实际情况: AI 不会主动识别规范漂移,需要在 CI 中设置强制检查点,或使用 AI 定期执行规范审计任务。
九、总结:融合方法论的核心思想
三句话总结:
- SDD 是 AI 的"航线图":先定义清楚要去哪里,AI 才能飞得准。
- TDD 是 AI 的"落地检查":飞到了之后,测试告诉你有没有偏航。
- 融合是必然趋势:AI Coding 不是替代工程实践,而是让工程实践的价值被放大。
在 AI 时代,代码的质量取决于规范的质量和测试的密度,而不仅仅是 AI 模型的能力。工程师的核心竞争力,正在从"能写代码"转变为"能定义规范、能设计测试、能驾驭 AI"。
参考资料
- Test-Driven Development: By Example - Kent Beck
- OpenAPI Specification 3.1
- Specification-Driven Development with AI
- Pact Contract Testing
- Spectral OpenAPI Linter
- 《规范驱动开发·以Skill为核心的智能交付》—— 泰合产品本部内部分享
本文首发于 CSDN,转载请注明出处。如有技术交流,欢迎评论区留言。

392

被折叠的 条评论
为什么被折叠?



