Skip to content

ATDD:验收测试驱动开发

什么是 ATDD

验收测试驱动开发(Acceptance Test-Driven Development)是 TDD 的延伸,由 Paul Duvall 等人在敏捷实践中推广。它的核心思想是:

在写任何代码之前,先用可执行的验收测试来定义"完成"的标准。

ATDD 与 TDD 的区别在于层次:

维度TDDATDD
视角开发者视角业务/用户视角
测试粒度单元测试端到端/功能测试
测试语言代码接近自然语言
参与者开发者开发+产品+测试
目标代码正确性功能符合需求

ATDD 把验收测试当作团队的"共同编程语言"——产品经理、测试工程师和开发者用同一套测试标准沟通需求。


BDD + AI 的黄金组合

行为驱动开发(BDD)是 ATDD 的一种流行实现形式,使用 Gherkin 语法(Given/When/Then)编写可读性极高的测试规格。

这种语法对 AI 来说是极佳的输入格式:

  • 结构化:Given/When/Then 强迫你把需求拆解为前置条件、操作和期望结果
  • 无歧义:比自然语言更精确,比代码更易读
  • 可直接用作 Prompt:Gherkin 场景可以直接粘贴给 AI 作为实现指令

Gherkin 语法速览

gherkin
Feature: 用户登录

  Scenario: 正确密码登录成功
    Given 用户 "alice@example.com" 已注册,密码为 "secret123"
    When 用户用邮箱 "alice@example.com" 和密码 "secret123" 尝试登录
    Then 登录应该成功
    And 返回有效的 JWT token

  Scenario: 错误密码登录失败
    Given 用户 "alice@example.com" 已注册
    When 用户用邮箱 "alice@example.com" 和密码 "wrongpass" 尝试登录
    Then 登录应该失败
    And 返回错误信息 "邮箱或密码错误"
    And 不返回任何 token

ATDD + AI 工作流

产品/团队 → 定义验收标准(用户故事 + 场景)

你 → 将验收标准转写为 Gherkin 规格

AI → 根据 Gherkin 生成测试代码(步骤定义)

你 → 运行测试,确认失败(需求未实现)

AI → 逐步实现功能,直到所有验收测试通过

你 → Code Review + 确认绿灯

完成!

实战示例:电商订单状态机

第一步:定义验收标准

与产品讨论后,整理出以下用户故事:

作为买家,我希望订单状态能正确流转,
以便我了解我的购买进度。

验收标准:
1. 新创建的订单状态为"待支付"
2. 支付成功后状态变为"已支付"
3. 已支付的订单可以发货,状态变为"已发货"
4. 已发货的订单可以确认收货,状态变为"已完成"
5. 待支付状态可以取消,状态变为"已取消"
6. 已支付/已发货状态不能直接取消
7. 已完成/已取消的订单不能进行任何状态变更

第二步:转写为 Gherkin

gherkin
Feature: 订单状态流转

  Background:
    Given 系统中存在一个有效用户

  Scenario: 新订单默认为待支付状态
    When 用户创建一个新订单
    Then 订单状态应为 "PENDING_PAYMENT"

  Scenario: 支付成功后订单状态更新
    Given 用户有一个状态为 "PENDING_PAYMENT" 的订单
    When 支付成功回调触发
    Then 订单状态应为 "PAID"

  Scenario: 已支付订单可以发货
    Given 用户有一个状态为 "PAID" 的订单
    When 商家执行发货操作
    Then 订单状态应为 "SHIPPED"

  Scenario: 已发货订单确认收货
    Given 用户有一个状态为 "SHIPPED" 的订单
    When 用户确认收货
    Then 订单状态应为 "COMPLETED"

  Scenario: 待支付订单可以取消
    Given 用户有一个状态为 "PENDING_PAYMENT" 的订单
    When 用户取消订单
    Then 订单状态应为 "CANCELLED"

  Scenario Outline: 不可取消的订单状态
    Given 用户有一个状态为 "<status>" 的订单
    When 用户尝试取消订单
    Then 操作应该失败
    And 错误信息应包含 "当前订单状态不允许取消"

    Examples:
      | status    |
      | PAID      |
      | SHIPPED   |
      | COMPLETED |
      | CANCELLED |

第三步:让 AI 生成测试代码

将以上 Gherkin 发给 AI,要求用 TypeScript + Vitest 实现(不使用 Cucumber,直接翻译为测试用例):

typescript
// order-state.test.ts
import { describe, it, expect, beforeEach } from 'vitest'
import { Order, OrderStatus } from './order'

describe('订单状态流转', () => {
  let order: Order

  describe('新订单默认为待支付状态', () => {
    it('新创建的订单状态为 PENDING_PAYMENT', () => {
      order = new Order({ userId: 'user-1' })
      expect(order.status).toBe(OrderStatus.PENDING_PAYMENT)
    })
  })

  describe('支付流转', () => {
    beforeEach(() => {
      order = new Order({ userId: 'user-1' })
    })

    it('支付成功后状态变为 PAID', () => {
      order.pay()
      expect(order.status).toBe(OrderStatus.PAID)
    })

    it('已支付订单发货后状态变为 SHIPPED', () => {
      order.pay()
      order.ship()
      expect(order.status).toBe(OrderStatus.SHIPPED)
    })

    it('已发货订单确认收货后状态变为 COMPLETED', () => {
      order.pay()
      order.ship()
      order.complete()
      expect(order.status).toBe(OrderStatus.COMPLETED)
    })
  })

  describe('取消逻辑', () => {
    it('待支付订单可以取消', () => {
      order = new Order({ userId: 'user-1' })
      order.cancel()
      expect(order.status).toBe(OrderStatus.CANCELLED)
    })

    it.each([
      OrderStatus.PAID,
      OrderStatus.SHIPPED,
      OrderStatus.COMPLETED,
      OrderStatus.CANCELLED,
    ])('状态为 %s 的订单不能取消', (status) => {
      order = Order.fromStatus(status)
      expect(() => order.cancel()).toThrow('当前订单状态不允许取消')
    })
  })
})

第四步:AI 生成实现代码

typescript
// order.ts
export enum OrderStatus {
  PENDING_PAYMENT = 'PENDING_PAYMENT',
  PAID = 'PAID',
  SHIPPED = 'SHIPPED',
  COMPLETED = 'COMPLETED',
  CANCELLED = 'CANCELLED',
}

interface OrderOptions {
  userId: string
  status?: OrderStatus
}

export class Order {
  status: OrderStatus
  userId: string

  constructor({ userId, status = OrderStatus.PENDING_PAYMENT }: OrderOptions) {
    this.userId = userId
    this.status = status
  }

  static fromStatus(status: OrderStatus): Order {
    return new Order({ userId: 'test-user', status })
  }

  pay(): void {
    this.assertStatus(OrderStatus.PENDING_PAYMENT, 'pay')
    this.status = OrderStatus.PAID
  }

  ship(): void {
    this.assertStatus(OrderStatus.PAID, 'ship')
    this.status = OrderStatus.SHIPPED
  }

  complete(): void {
    this.assertStatus(OrderStatus.SHIPPED, 'complete')
    this.status = OrderStatus.COMPLETED
  }

  cancel(): void {
    const cancellable = [OrderStatus.PENDING_PAYMENT]
    if (!cancellable.includes(this.status)) {
      throw new Error('当前订单状态不允许取消')
    }
    this.status = OrderStatus.CANCELLED
  }

  private assertStatus(expected: OrderStatus, operation: string): void {
    if (this.status !== expected) {
      throw new Error(
        `操作 ${operation} 需要订单状态为 ${expected},当前状态为 ${this.status}`
      )
    }
  }
}

ATDD 在 Vibe Coding 中的价值

Builder.io 的实践表明,在 AI 辅助开发中,验收测试充当了"意图锚点"的角色:

1. 防止 AI 漂移

长对话中,AI 可能逐渐偏离最初的需求。验收测试是客观标准,测试通过了就是通过了,不受对话上下文影响。

2. 分解复杂需求

一个功能可以有多个 Gherkin 场景。每次让 AI 只实现一个场景,迭代推进,比一次性塞给 AI 所有需求更可靠。

3. 文档即测试

Gherkin 既是测试,也是活文档。产品经理可以直接阅读测试文件理解系统行为,减少沟通成本。


小结

ATDD 把需求澄清的工作提前,把"完成"的定义变得精确可测。配合 AI,这个过程变得极其高效:你定义场景,AI 生成测试和实现,测试保证 AI 不偏题。这是 Vibe Coding 中最值得建立的工程习惯之一。