Files
gym-manage/docs/superpowers/specs/2026-04-08-user-journey-test-improvement-design.md

9.6 KiB
Raw Permalink Blame History

User Journey 测试改进设计文档

文档日期: 2026-04-08
负责人: 张翔
版本: 1.0
状态: 已验证


执行摘要

通过快速验证测试,我们确认了 Playwright 本身是有效的,问题在于测试方式。改进后的测试方法成功发现了真实问题,证明了方案的可行性。

核心发现

  • Playwright 工具本身有效
  • 旧测试方式存在假阳性问题
  • 新测试方式能真实发现问题
  • 三层验证策略可行

1. 问题分析

1.1 当前问题

用户报告

  • 测试通过了,但实际运行时页面没有内容
  • Console 有 Mock API 日志,但页面无内容

根本原因

// ❌ 错误的测试方式
const dataStats = page.locator('[data-testid="data-stats"]')
if (await dataStats.isVisible()) {  // 如果不可见,跳过验证!
  const statsText = await dataStats.textContent()
  expect(statsText).toBeTruthy()  // 这行永远不会执行
}
// 测试通过!但实际上什么都没验证

问题本质

  • 软验证:元素不存在就跳过验证
  • 假阳性:测试通过但实际无效
  • 缺乏强制验证:没有确保元素必须存在

1.2 验证测试结果

测试文件: tests/e2e/specs/validation/test-improvement-validation.spec.ts

测试结果

测试类型 结果 说明
旧方式:软验证 通过 假阳性! 元素不存在但测试通过
新方式:硬验证 失败 正确! 元素不存在,测试失败
三层验证 失败 API请求超时,暴露真实问题
完整用户旅程 失败 案件列表元素不存在(count = 0)
诊断测试 通过 提供详细诊断信息

关键发现

  • 页面上没有 .ant-list-item 元素(count = 0
  • API请求超时(没有调用 /api/cases
  • 页面根本没有加载案件数据

2. 解决方案

2.1 核心原则转变

旧方式(软验证)

// 软验证:元素不存在就跳过
if (await element.isVisible()) {
  expect(await element.textContent()).toBeTruthy()
}

新方式(硬验证)

// 硬验证:元素必须存在且可见
await expect(element).toBeVisible()
const text = await element.textContent()
expect(text).toBeTruthy()
expect(text.length).toBeGreaterThan(0)

2.2 三层验证策略

test('真实验证用户看到的内容', async ({ page }) => {
  // Layer 1: API层验证
  const response = await page.waitForResponse('**/api/cases')
  expect(response.status()).toBe(200)
  const data = await response.json()
  expect(data.length).toBeGreaterThan(0)
  
  // Layer 2: 状态层验证
  const state = await page.evaluate(() => {
    return {
      cases: window.__CASE_STORE__?.getState().cases,
      currentCase: window.__CASE_STORE__?.getState().currentCase
    }
  })
  expect(state.cases.length).toBeGreaterThan(0)
  
  // Layer 3: DOM层验证
  const caseItems = page.locator('.ant-list-item')
  await expect(caseItems.first()).toBeVisible({ timeout: 5000 })
  const count = await caseItems.count()
  expect(count).toBeGreaterThan(0)
  
  // Layer 4: 内容验证
  const firstCaseText = await caseItems.first().textContent()
  expect(firstCaseText).toBeTruthy()
  expect(firstCaseText.length).toBeGreaterThan(10)
})

2.3 增强的测试工具

1. 状态验证器

// tests/e2e/utils/state-validator.ts
export async function validatePageState(page: Page, expectedState: {
  hasCase?: boolean
  hasData?: boolean
  activePage?: string
}) {
  const state = await page.evaluate(() => ({
    currentCase: window.__CASE_STORE__?.getState().currentCase,
    transactions: window.__DATA_STORE__?.getState().transactions,
    activePage: window.__PAGE_STORE__?.getState().activePageKey
  }))
  
  if (expectedState.hasCase) {
    expect(state.currentCase).toBeTruthy()
  }
  if (expectedState.hasData) {
    expect(state.transactions.length).toBeGreaterThan(0)
  }
  if (expectedState.activePage) {
    expect(state.activePage).toBe(expectedState.activePage)
  }
}

2. 内容验证器

// tests/e2e/utils/content-validator.ts
export async function validateContent(
  page: Page, 
  selector: string, 
  options: {
    mustBeVisible?: boolean
    mustHaveText?: boolean
    minLength?: number
    exactText?: string
  } = {}
) {
  const element = page.locator(selector)
  
  // 默认必须可见
  if (options.mustBeVisible !== false) {
    await expect(element).toBeVisible({ timeout: 5000 })
  }
  
  if (options.mustHaveText) {
    const text = await element.textContent()
    expect(text).toBeTruthy()
    
    if (options.minLength) {
      expect(text.length).toBeGreaterThanOrEqual(options.minLength)
    }
    
    if (options.exactText) {
      expect(text.trim()).toBe(options.exactText)
    }
  }
}

3. 截图验证器

// tests/e2e/utils/screenshot-validator.ts
export async function takeScreenshotAndValidate(
  page: Page,
  testName: string,
  step: string
) {
  const screenshot = await page.screenshot({
    fullPage: true,
    path: `test-results/screenshots/${testName}-${step}.png`
  })
  
  // 验证截图不为空
  expect(screenshot.length).toBeGreaterThan(1000)
  
  console.log(`📸 Screenshot saved: ${testName}-${step}.png`)
}

3. 实施计划

3.1 短期(1周内)

目标: 修复现有测试用例

任务清单:

  • 将所有软验证改为硬验证
  • 添加三层验证策略
  • 创建测试工具函数
  • 修复发现的问题

预计工作量: 2-3 天


3.2 中期(2-4周)

目标: 建立完整的测试体系

任务清单:

  • 添加视觉验证(截图对比)
  • 建立测试报告机制
  • 集成到CI/CD
  • 建立测试数据管理

预计工作量: 5-7 天


3.3 长期(1-3个月)

目标: 持续优化和扩展

任务清单:

  • 评估是否需要引入Storybook
  • 考虑AI辅助测试
  • 建立性能测试
  • 建立安全测试

预计工作量: 10-15 天


4. 技术选型

4.1 核心工具

Playwright 已验证有效

  • 优势:

    • 强大的选择器和断言
    • 支持API拦截和验证
    • 内置截图和视频录制
    • 跨浏览器支持
    • 活跃的社区和文档
  • 劣势:

    • 需要正确的使用方式
    • 学习曲线适中

结论: 继续使用Playwright,改进测试方式


4.2 辅助工具

工具 用途 优先级
Playwright Screenshot 视觉验证 P0
Playwright Trace 调试支持 P0
Playwright API Mocking 数据模拟 P1
Percy / Chromatic 视觉回归 P2
Storybook 组件测试 P3

5. 质量保障

5.1 测试原则

  1. 硬验证优先: 元素必须存在,否则测试失败
  2. 多层验证: API → 状态 → DOM → 内容
  3. 快速失败: 发现问题立即失败,不继续执行
  4. 清晰诊断: 提供详细的诊断信息

5.2 测试覆盖率目标

层级 当前覆盖率 目标覆盖率
API层 0% 100%
状态层 0% 100%
DOM层 50% 100%
内容层 30% 100%
总体 30% 100%

6. 风险评估

6.1 技术风险

风险 影响 概率 缓解措施
测试用例维护成本高 建立测试工具库,提高可维护性
测试执行时间长 使用并行执行,优化测试用例
误报率高 使用硬验证,减少假阳性

6.2 业务风险

风险 影响 概率 缓解措施
测试不通过影响交付 优先修复关键问题,建立分级测试
测试数据管理复杂 建立测试数据工厂,使用Mock数据

7. 成功标准

7.1 短期目标(1周内)

  • 所有测试用例使用硬验证
  • 测试覆盖率提升到 60%
  • 无假阳性问题
  • 发现并修复当前问题

7.2 中期目标(2-4周)

  • 测试覆盖率提升到 80%
  • 建立完整的测试报告
  • 集成到CI/CD
  • 测试执行时间 < 10分钟

7.3 长期目标(1-3个月)

  • 测试覆盖率提升到 100%
  • 建立视觉回归测试
  • 建立性能测试
  • 测试执行时间 < 5分钟

8. 附录

8.1 验证测试文件

文件: tests/e2e/specs/validation/test-improvement-validation.spec.ts

测试结果:

  • 旧方式:软验证 - 通过(假阳性)
  • 新方式:硬验证 - 失败(正确)
  • 三层验证 - 失败(正确)
  • 完整用户旅程 - 失败(正确)
  • 诊断测试 - 通过

8.2 参考资料


9. 总结

9.1 核心结论

  1. Playwright 工具本身有效
  2. 问题在于测试方式(软验证 vs 硬验证)
  3. 改进后的测试能真实发现问题
  4. 三层验证策略可行

9.2 下一步行动

  1. 立即行动: 修复现有测试用例,使用硬验证
  2. 短期计划: 建立测试工具库,提高可维护性
  3. 中期计划: 集成到CI/CD,建立完整测试体系
  4. 长期计划: 持续优化,建立视觉回归测试

文档状态: 已验证
下一步: 用户审查书面规格