Skip to content

自动化测试策略

AI 编程时代的测试策略要解决两个问题:第一,证明功能真的符合验收标准;第二,约束 AI 生成代码不要悄悄引入回归。测试不是最后补的文档,而是 AI 任务的一部分。

测试金字塔

text
          E2E tests        少量,覆盖关键用户路径
       Integration tests   适量,覆盖模块和 API 交互
          Unit tests       大量,覆盖纯逻辑和边界条件

建议比例:

层级比例适合 AI 生成吗说明
Unit60%-70%很适合输入输出明确,容易审查
Integration20%-30%适合需要理解模块边界
E2E5%-10%谨慎容易受 UI 变化影响

docs/project/test-strategy.md 模板

markdown
# Test Strategy

## Scope

This strategy covers the AI project delivery dashboard v1.

## Test Goals

- Verify task creation, update, delete, and filtering.
- Verify dashboard summary counts.
- Verify localStorage persistence.
- Verify empty, loading, and error-like states where applicable.

## Unit Tests

Target:

- Task summary calculation.
- Task filtering.
- Task validation.
- localStorage serialization helpers.

Command:

`npm run test`

## Integration Tests

Target:

- Task form updates task list.
- Status change updates dashboard cards.
- Reset action restores seed data.

Command:

`npm run test`

## E2E Smoke Test

Target:

- Open dashboard.
- Create a task.
- Mark it blocked.
- Confirm blocked count updates.
- Refresh page and confirm data remains.

Command:

`npm run test:e2e`

## Quality Gate

Before a change is complete:

- Unit tests pass.
- Build passes.
- At least one browser smoke path is manually verified if E2E is unavailable.
- Known untested areas are listed in the final report.

单元测试示例

typescript
import { describe, expect, it } from 'vitest'
import { summarizeTasks } from './summarizeTasks'

describe('summarizeTasks', () => {
  it('counts tasks by status', () => {
    const result = summarizeTasks([
      { id: '1', status: 'todo' },
      { id: '2', status: 'in_progress' },
      { id: '3', status: 'blocked' },
      { id: '4', status: 'done' },
      { id: '5', status: 'blocked' },
    ])

    expect(result.total).toBe(5)
    expect(result.todo).toBe(1)
    expect(result.inProgress).toBe(1)
    expect(result.blocked).toBe(2)
    expect(result.done).toBe(1)
  })
})

集成测试示例

typescript
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { TaskDashboard } from './TaskDashboard'

it('creates a task and updates the dashboard count', async () => {
  render(<TaskDashboard />)

  await userEvent.type(screen.getByLabelText('Task title'), 'Prepare release checklist')
  await userEvent.selectOptions(screen.getByLabelText('Priority'), 'high')
  await userEvent.click(screen.getByRole('button', { name: 'Create task' }))

  expect(screen.getByText('Prepare release checklist')).toBeInTheDocument()
  expect(screen.getByTestId('total-count')).toHaveTextContent('1')
})

E2E Smoke 示例

typescript
import { expect, test } from '@playwright/test'

test('task delivery smoke flow', async ({ page }) => {
  await page.goto('/')

  await page.getByLabel('Task title').fill('Fix login edge case')
  await page.getByLabel('Owner').fill('Dev A')
  await page.getByLabel('Priority').selectOption('high')
  await page.getByRole('button', { name: 'Create task' }).click()

  await page.getByText('Fix login edge case').click()
  await page.getByLabel('Status').selectOption('blocked')
  await page.getByLabel('Blocker reason').fill('Need API contract confirmation')
  await page.getByRole('button', { name: 'Save task' }).click()

  await expect(page.getByTestId('blocked-count')).toHaveText('1')
})

给 Claude Code / Codex 的测试提示

text
请先阅读 docs/project/test-strategy.md。
为当前变更补最小必要测试。
不要为了通过测试而降低验收标准。
完成后运行测试和构建,并说明未覆盖风险。

测试完成标准

  • 测试覆盖核心业务逻辑,而不是只覆盖渲染存在。
  • 至少有一个失败用例能证明边界被处理。
  • 测试名称描述用户行为或业务规则。
  • 测试不依赖执行顺序。
  • 测试数据不污染真实数据。

常见反模式

反模式问题替代做法
只测快照难以证明行为测用户行为和状态变化
E2E 过多慢且脆弱关键路径保留 E2E,逻辑下沉到单元测试
Mock 一切集成风险被隐藏对关键模块做少量集成测试
没有负向用例边界缺陷遗漏加非法输入、空状态、权限失败
AI 生成后不审查断言可能无意义人类检查断言是否对应验收

课堂练习

  1. 让 AI 读取 acceptance.mdtest-strategy.md
  2. 要求 AI 先列出测试清单,不写代码。
  3. 选择 2 个单元测试和 1 个 smoke 测试实现。
  4. 故意改坏一个业务规则,确认测试会失败。
  5. 恢复代码并运行完整验证。