整体测试架构

┌─────────────────────────────────────────────┐
│              AI 测试引擎层                    │
│  Spec → 用例生成 → 数据生成 → 失败分析       │
└──────────────┬──────────────────────────────┘
               │ 生成 .test.ts 文件 + fixtures
       ┌───────┴────────┐
       ▼                ▼
┌──────────────┐ ┌──────────────┐
│   Vitest     │ │  Playwright  │
│  单测/集成    │ │   E2E/验收    │
│  (快速反馈)   │ │  (真实场景)   │
└──────────────┘ └──────────────┘
       │                │
       ▼                ▼
   组件/API级别      页面/流程级别

核心原则:Vitest 管单元/集成,Playwright 管端到端,各跑各的,不要互相嵌套。

三层测试体系

第1层:Vitest — 单元测试 + API 集成测试

// AI 生成的 API 接口测试示例
// tests/api/employee.test.ts
import { describe, it, expect, beforeAll } from 'vitest'
import { createTestClient } from '../helpers/testClient'

describe('POST /api/v1/employee', () => {
  let client: TestClient

  beforeAll(() => {
    client = createTestClient()
  })

  // AI 从 Spec 中的 TC001 自动生成
  it('TC001: 通过ID精确查询成功', async () => {
    const res = await client.post('/api/v1/employee?id=E001')
    expect(res.status).toBe(200)
    expect(res.body.code).toBe(0)
    expect(res.body.data.id).toBe('E001')
    expect(res.body.data).toHaveProperty('gender')
    expect(res.body.data).toHaveProperty('aqfsmcjks')
  })

  // AI 从 Spec 中的 TC004 自动生成
  it('TC004: 缺少必填参数应返回400', async () => {
    const res = await client.post('/api/v1/employee')
    expect(res.status).toBe(400)
    expect(res.body.code).toBe(400)
  })

  // AI 从 Spec 中的 TC010 自动生成 — 安全测试
  it('TC010: SQL注入防护', async () => {
    const res = await client.post("/api/v1/employee?name=张' OR '1'='1")
    expect(res.status).not.toBe(500)
  })
})

Vitest 的优势:

  • 速度快,毫秒级反馈
  • 原生 ESM + TypeScript
  • 和 Vite 项目零配置集成
  • 适合 API 级别的集成测试(用 supertest 或 fetch mock)

第2层:Playwright — E2E 验收测试

// e2e/employee.spec.ts — AI 从 UI Spec + 测试流程文档生成
import { test, expect } from '@playwright/test'

test.describe('员工信息查询 - E2E验收', () => {

  // AI 从测试流程文档的"测试准备"自动生成 beforeEach
  test.beforeEach(async ({ page }) => {
    await page.goto('/login')
    await page.fill('[data-testid="username"]', 'test_admin')
    await page.fill('[data-testid="password"]', 'test_pass')
    await page.click('[data-testid="login-btn"]')
    await page.waitForURL('/dashboard')
  })

  // AI 从 Spec + UI 设计文档联合生成
  test('TC001: 通过ID精确查询 - 完整用户流程', async ({ page }) => {
    await page.click('[data-testid="nav-employee"]')
    await page.fill('[data-testid="search-id"]', 'E001')
    await page.click('[data-testid="search-btn"]')

    // 验证结果表格
    await expect(page.locator('[data-testid="result-table"]')).toBeVisible()
    await expect(page.locator('[data-testid="result-id"]')).toHaveText('E001')
  })

  // AI 生成边界场景
  test('TC004: 空查询应显示错误提示', async ({ page }) => {
    await page.click('[data-testid="nav-employee"]')
    await page.click('[data-testid="search-btn"]') // 不填任何参数

    await expect(page.locator('[data-testid="error-msg"]'))
      .toContainText('请提供员工ID或姓名')
  })
})

第3层:AI 驱动 — 用例生成 + 数据生成 + 失败分析

这是最关键的部分,决定了整个测试体系能不能"闭环"。

AI 测试引擎设计

1. 用例生成:Spec → Test Code

// ai-test-engine/generator.ts
interface SpecTestCase {
  id: string          // TC001
  module: string      // 功能/异常/边界/安全
  scenario: string    // 测试场景描述
  preconditions: string
  steps: string[]
  expected: string[]
  priority: string    // P0/P1/P2
}

interface GenerateOptions {
  spec: APISpec          // 结构化的 API Spec
  testCases: SpecTestCase[]  // Spec 中的测试用例表
  target: 'vitest' | 'playwright'
  baseUrl: string
  dataTestIds?: Record<string, string>  // UI 元素映射表
}

async function generateTests(options: GenerateOptions): Promise<GeneratedSuite> {
  // 按优先级分层
  const p0Cases = options.testCases.filter(tc => tc.priority === 'P0')
  const p1Cases = options.testCases.filter(tc => tc.priority === 'P1')

  // 生成 Vitest 测试(API 级别)
  if (options.target === 'vitest') {
    return generateVitestSuite(options.spec, p0Cases.concat(p1Cases))
  }

  // 生成 Playwright 测试(E2E 级别)
  if (options.target === 'playwright') {
    return generatePlaywrightSuite(options.spec, p0Cases, options.dataTestIds!)
  }
}

Prompt 模板:

你是一个资深测试工程师。根据以下 API Spec 和测试用例,生成 {target} 测试代码。
要求:
1. 严格使用 TypeScript
2. 每个测试用例对应一个 it/test 块
3. 断言必须覆盖 expected 中的每一项
4. 使用 data-testid 定位元素(Playwright)
5. 测试数据使用 fixture 文件,不要硬编码

API Spec:
{spec}

测试用例:
{testCases}

输出格式:完整的 .test.ts 文件内容

2. 数据生成:Spec → Fixtures

// ai-test-engine/data-generator.ts
// AI 根据 DB 设计文档 + API Spec 生成测试数据

// 生成的 fixtures/employee.ts
export const employeeFixtures = {
  // 正常数据
  validEmployee: {
    id: 'E001',
    name: '张三丰',
    gender: '男',
    aqfsmcjks: 100001,
    cardNo: '6222021234567890',
    status: 1
  },

  // 边界数据 — AI 从测试用例推导
  boundaryCases: {
    emptyId: { id: '', name: '张三' },
    emptyName: { id: 'E001', name: '' },
    bothEmpty: { id: '', name: '' },
    sqlInjection: { name: "张' OR '1'='1" },
    longName: { name: '张'.repeat(200) },
    specialChars: { name: '<script>alert(1)</script>' }
  },

  // 性能测试数据 — AI 批量生成
  bulkEmployees: Array.from({ length: 100000 }, (_, i) => ({
    id: `E${String(i).padStart(5, '0')}`,
    name: `员工${i}`,
    gender: i % 2 === 0 ? '男' : '女',
    aqfsmcjks: Math.floor(Math.random() * 1000000),
    status: 1
  }))
}

3. 失败分析:Test Result → Fix Suggestion

// ai-test-engine/failure-analyzer.ts

interface TestFailure {
  testCaseId: string
  testName: string
  expected: string
  actual: string
  errorStack: string
  screenshot?: string   // Playwright 截图
  networkLog?: object[] // Playwright 网络日志
}

async function analyzeFailure(failure: TestFailure): Promise<FixSuggestion> {
  // AI 分析失败原因,返回:
  // 1. 根因分析(参数校验缺失?SQL注入?字段映射错误?)
  // 2. 建议的代码修改
  // 3. 关联的 Spec 条目(用于回环到阶段4)
  // 4. 影响范围(哪些其他用例可能受影响)
}

Prompt 模板:

以下是测试失败报告:
测试用例: {testCaseId} - {testName}
预期结果: {expected}
实际结果: {actual}
错误堆栈: {errorStack}
网络请求: {networkLog}

请分析:
1. 根因是什么?(代码bug / Spec定义不清 / 测试用例错误)
2. 如果是代码bug,给出修复建议(精确到文件和行号)
3. 如果是Spec问题,给出Spec修改建议
4. 哪些其他测试用例可能受影响?

相关 Spec:
{spec}

完整的测试流水线

# .github/workflows/ai-test.yml
name: AI-Driven Test Pipeline

on: [push, pull_request]

jobs:
  unit-api-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install deps
        run: npm ci
      - name: Run Vitest (Unit + API)
        run: npx vitest run --reporter=json --outputFile=results/vitest.json
      - name: AI Analyze Failures
        if: failure()
        run: npx ts-node ai-test-engine/analyze-ci.ts --input results/vitest.json

  e2e-acceptance:
    runs-on: ubuntu-latest
    needs: unit-api-test
    steps:
      - uses: actions/checkout@v4
      - name: Install Playwright
        run: npx playwright install --with-deps
      - name: Run E2E
        run: npx playwright test --reporter=json
      - name: Upload artifacts on failure
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: e2e-failures
          path: |
            test-results/
            playwright-report/
      - name: AI Analyze E2E Failures
        if: failure()
        run: npx ts-node ai-test-engine/analyze-ci.ts --input results/playwright.json --screenshots test-results/

关键技术选型建议

环节工具理由
单测/集成Vitest快、原生 TS、和 Vite 项目零配置
E2E/验收Playwright多浏览器、自动截图、网络拦截、trace viewer
API MockMSW (Mock Service Worker)Vitest 和 Playwright 都能用,一套 mock 两边跑
测试数据Fixtures + AI 生成不硬编码,AI 根据 Spec 生成边界数据
失败截图Playwright 内置自动截图 + trace,AI 可直接分析
测试报告Playwright HTML Report + 自定义 AI 分析人看 HTML 报告,AI 读 JSON 结果

需要注意的坑

1. Playwright 和 Vitest 不要混用同一个 runner

// ❌ 不要这样做
import { test } from 'vitest'
import { chromium } from 'playwright'

test('E2E', async () => {
  const browser = await chromium.launch()
  // ...
})

// ✅ 正确做法:E2E 用 @playwright/test
import { test } from '@playwright/test'

2. data-testid 必须在阶段5代码生成时就埋好

如果 AI 生成的前端代码没有 data-testid,Playwright 测试就无法稳定定位元素。这要求阶段5的代码生成 Prompt 必须包含"为所有交互元素添加 data-testid"的要求。

3. 测试数据隔离

每个测试用例必须能独立运行,不能依赖执行顺序。用 beforeEach 重置状态,不要用 beforeAll 共享可变数据。

总结

Playwright + Vitest + AI 测试框架的核心是三层分离——Vitest 管快、Playwright 管真、AI 管生成和分析。最大的工程化挑战不在框架选型,而在确保阶段5生成的代码有 data-testid、API 有稳定契约,这样测试才能自动化闭环。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐