【AI·Coding】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 核心对比

维度TDDSDD
核心产物测试用例规范文档
约束时机运行时(测试执行)编码前(上下文输入)
AI 协作方式AI 实现满足测试的代码AI 根据规范生成代码
质量保障层功能正确性结构一致性
适用场景业务逻辑、算法层接口层、架构层
维护成本测试维护成本高规范维护成本高
AI 友好度中(需精准描述测试意图)高(结构化文本天然适合 AI 解析)

三、整体架构:TDD + SDD 融合的 AI Coding 体系

失败反馈

规范不一致

测试通过

🔄 持续集成层

规范 Lint 校验

测试自动执行

覆盖率检测

规范漂移告警

✅ 验证层 (TDD)

单元测试 Unit Test

集成测试 Integration

契约测试 Contract

E2E 测试

🤖 AI 编码层

AI 解析规范上下文

AI 生成代码骨架

AI 补全实现逻辑

AI 自动生成测试

📋 规范层 (SDD)

需求文档 / PRD

接口规范 OpenAPI

类型定义 TypeScript

架构决策 ADR

AI 任务规范 SKILL.md

🚀 部署


四、实践流程:五步融合法

Step 1:需求 → 规范(SDD 起点)

在拿到需求后,不要急于让 AI 写代码,先将需求转化为结构化规范:

规范文件 AI 助手 开发者 产品经理 规范文件 AI 助手 开发者 产品经理 openapi.yaml / types.ts / ADR.md 提交需求文档 (PRD) 请解析需求,生成 OpenAPI 草稿 返回 OpenAPI YAML 草稿 审核 & 提交规范文件 基于规范生成代码骨架

实操示例: 以"用户登录接口"为例

# 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 规范

🧪 测试骨架生成

正常路径测试

边界条件测试

异常路径测试

性能基准测试

全部失败
Red 状态

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 开始实现

读取约束

📋 OpenAPI 规范
接口契约约束

🧪 单元测试
行为约束

AI 生成实现代码

运行测试

测试通过?

AI 分析失败原因

失败类型

重新解析规范

Green 状态

AI 执行重构

再次运行测试确认

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:重构与规范更新(双向反馈)

代码通过测试后,进入重构阶段,同时维护规范的准确性:

SDD → TDD

测试驱动

红→绿

Refactor

实现反哺规范

发现规范不完整

规范变化→新测试

无需更新

规范与实现一致

确认绿灯

合并主干

规范制定

测试编写

AI实现

测试通过

代码重构

规范检视

规范更新

测试补充

CI集成


Step 5:CI 流水线中的规范守护

将 SDD 和 TDD 的质量门控集成到 CI/CD 流水线:

结果

质量门控

触发器

全部通过

任一失败

任一失败

任一失败

任一失败

任一失败

任一失败

Pull Request

📋 规范校验
openapi-lint
spectral

🔍 类型检查
tsc --noEmit

🧪 单元测试
jest --coverage

📊 覆盖率门槛
>= 80%

🔄 契约测试
Pact Broker

🤖 AI 规范漂移检测
实现 vs 规范 diff

✅ 准入合并

阻断合并
返回详细报告


五、完整实践案例:云安全告警管理模块

以一个真实场景为例:云安全平台的告警规则管理接口,演示完整的 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 面临的最大挑战:

Sprint 1 规范与实现完全一致 OpenAPI v1.0 = 实际接口 Sprint 3 快速迭代导致规范更新滞后 实现新增字段未更新规范 Sprint 6 规范成为"装饰" AI 生成代码开始偏离预期 Sprint 8 技术债爆发 新人困惑·测试失败·AI 幻觉增加 规范漂移的典型演进路径

治理方案:三道防线

通过

通过

漂移告警

阻断

阻断

阻断

第三道防线:AI 主动感知

PR 提交时 AI diff 分析

实现与规范语义对比

自动生成规范更新 PR

第二道防线:运行时契约

Pact Consumer-Driven Contract

Spring Cloud Contract

自动生成契约测试

第一道防线:静态检测

spectral lint 规范格式检查

openapi-diff 检测接口变更

tsc 类型守门

开发者修复

PR 不可合并


七、工具链推荐

场景推荐工具说明
规范编写Stoplight Studio / Swagger Editor可视化 OpenAPI 编辑
规范 LintSpectral自定义规范校验规则
契约测试Pact.ioConsumer-Driven Contract
AI 编码WorkBuddy / Cursor结合 SKILL.md 使用
测试框架JUnit 5 + AssertJ(Java)/ Jest(TS)可读性强
覆盖率JaCoCo / IstanbulCI 集成门槛检查
规范版本管理Git + Conventional Commits规范变更可追溯
接口 MockWireMock / 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 定期执行规范审计任务。


九、总结:融合方法论的核心思想

TDD + SDD\nAI Coding

SDD 规范层

OpenAPI 接口约定

TypeScript 类型定义

ADR 架构决策

SKILL.md AI任务规范

TDD 验证层

单元测试

集成测试

契约测试

E2E 测试

AI 协作层

规范→代码骨架

测试→驱动实现

AI→补全逻辑

CI→守护质量

工程文化

规范即文档

测试即规格

AI 是工具

人是决策者

三句话总结:

  1. SDD 是 AI 的"航线图":先定义清楚要去哪里,AI 才能飞得准。
  2. TDD 是 AI 的"落地检查":飞到了之后,测试告诉你有没有偏航。
  3. 融合是必然趋势:AI Coding 不是替代工程实践,而是让工程实践的价值被放大。

在 AI 时代,代码的质量取决于规范的质量测试的密度,而不仅仅是 AI 模型的能力。工程师的核心竞争力,正在从"能写代码"转变为"能定义规范、能设计测试、能驾驭 AI"。


参考资料


本文首发于 CSDN,转载请注明出处。如有技术交流,欢迎评论区留言。

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值