Appearance
测试策略
测试金字塔
/\
/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 |
|---|---|---|
| 创建画布 → 添加节点 → 连线 → 生成 | P0 | canvas-system + ai-generation-flow |
| Workspace CRUD | P0 | workspace |
| 连线规则(自连、循环、重复) | P0 | canvas-system |
| 文本节点富文本编辑 | P0 | text-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 # 开发时持续运行