Skip to content

测试策略

测试金字塔

        /\
       /E2E\        Playwright — 核心用户旅程
      /------\
     / 集成   \     Vitest + @nuxt/test-utils — 组件 + Store + Composable
    /----------\
   /  单元      \   Vitest — 纯函数、业务规则
  /--------------\

原则: 纯函数优先单元测试;Store/Composable 用集成测试;用户可见行为用 E2E。

单元测试

位置: apps/web/test/unit/命名: {模块名}.test.ts适用范围: 零框架依赖的纯函数(flowValidation、数据转换、算法逻辑)

typescript
it('在存在 A→B 边时,添加 B→A 边应检测为循环', () => {
  // Arrange
  const edges = [{ source: 'a', target: 'b' }]
  const newEdge = { source: 'b', target: 'a' }

  // Act
  const result = validateEdge(edges, newEdge)

  // Assert
  expect(result).toEqual({ valid: false, reason: 'cycle' })
})

集成测试

位置: apps/web/test/nuxt/命名: {模块名}.test.ts适用范围: Pinia Store、Composable、依赖真实 DOM 的组件逻辑 运行命令: pnpm test:nuxt

Store 测试模式:

typescript
beforeEach(() => {
  setActivePinia(createPinia())
  localStorage.clear()
})

E2E 测试

位置: apps/web/tests/命名: {功能名}.spec.ts适用范围: 核心用户旅程,来源于 Spec 中的 BDD Scenarios

场景优先级对应 Spec
创建画布 → 添加节点 → 连线 → 生成P0canvas-system + ai-generation-flow
Workspace CRUDP0workspace
连线规则(自连、循环、重复)P0canvas-system
文本节点富文本编辑P0text-node

BDD Scenario 到 Playwright 的映射:

typescript
// tests/canvas-system.spec.ts
test('从工具栏添加节点后节点出现在画布中', async ({ page }) => {
  // Given: 画布编辑器已打开
  await page.goto('/canvas/test-id')

  // When: 点击工具栏 + 按钮并选择文本节点
  await page.click('[data-testid="toolbar-add"]')
  await page.click('[data-testid="node-type-text"]')

  // Then: 画布中出现文本节点
  await expect(page.locator('[data-testid="node-text"]')).toBeVisible()
})

data-testid 规范

所有可交互的 UI 元素必须挂载 data-testid

组件data-testid
工具栏添加按钮toolbar-add
节点类型选项node-type-{text|image|video}
节点卡片node-{type}
生成按钮node-generate
Prompt 输入node-prompt
节点状态指示node-status-{idle|generating|done|error}

覆盖率目标

层级目标重点
单元> 90%flowValidation、数据转换函数
集成> 70%Store 所有 public action
E2E核心旅程 100%P0 场景不遗漏

运行命令

bash
pnpm test              # 全量测试
pnpm test:unit         # 仅单元测试
pnpm test:nuxt         # 仅集成测试
pnpm test:e2e          # 仅 E2E
pnpm test:unit --watch # 开发时持续运行