refactor(backend): 重命名后端项目为 gym-manage-api,修改包名为 cn.novalon.gym.manage
This commit is contained in:
@@ -0,0 +1,404 @@
|
||||
# User Journey 测试改进设计文档
|
||||
|
||||
**文档日期**: 2026-04-08
|
||||
**负责人**: 张翔
|
||||
**版本**: 1.0
|
||||
**状态**: 已验证
|
||||
|
||||
---
|
||||
|
||||
## 执行摘要
|
||||
|
||||
通过快速验证测试,我们确认了 **Playwright 本身是有效的**,问题在于测试方式。改进后的测试方法成功发现了真实问题,证明了方案的可行性。
|
||||
|
||||
**核心发现**:
|
||||
- ✅ Playwright 工具本身有效
|
||||
- ❌ 旧测试方式存在假阳性问题
|
||||
- ✅ 新测试方式能真实发现问题
|
||||
- ✅ 三层验证策略可行
|
||||
|
||||
---
|
||||
|
||||
## 1. 问题分析
|
||||
|
||||
### 1.1 当前问题
|
||||
|
||||
**用户报告**:
|
||||
- 测试通过了,但实际运行时页面没有内容
|
||||
- Console 有 Mock API 日志,但页面无内容
|
||||
|
||||
**根本原因**:
|
||||
```typescript
|
||||
// ❌ 错误的测试方式
|
||||
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 核心原则转变
|
||||
|
||||
#### ❌ 旧方式(软验证)
|
||||
```typescript
|
||||
// 软验证:元素不存在就跳过
|
||||
if (await element.isVisible()) {
|
||||
expect(await element.textContent()).toBeTruthy()
|
||||
}
|
||||
```
|
||||
|
||||
#### ✅ 新方式(硬验证)
|
||||
```typescript
|
||||
// 硬验证:元素必须存在且可见
|
||||
await expect(element).toBeVisible()
|
||||
const text = await element.textContent()
|
||||
expect(text).toBeTruthy()
|
||||
expect(text.length).toBeGreaterThan(0)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.2 三层验证策略
|
||||
|
||||
```typescript
|
||||
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. 状态验证器
|
||||
```typescript
|
||||
// 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. 内容验证器
|
||||
```typescript
|
||||
// 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. 截图验证器
|
||||
```typescript
|
||||
// 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 参考资料
|
||||
|
||||
- [Playwright 官方文档](https://playwright.dev/)
|
||||
- [Playwright 最佳实践](https://playwright.dev/docs/best-practices)
|
||||
- [测试驱动开发(TDD)](https://en.wikipedia.org/wiki/Test-driven_development)
|
||||
|
||||
---
|
||||
|
||||
## 9. 总结
|
||||
|
||||
### 9.1 核心结论
|
||||
|
||||
1. ✅ **Playwright 工具本身有效**
|
||||
2. ❌ **问题在于测试方式(软验证 vs 硬验证)**
|
||||
3. ✅ **改进后的测试能真实发现问题**
|
||||
4. ✅ **三层验证策略可行**
|
||||
|
||||
---
|
||||
|
||||
### 9.2 下一步行动
|
||||
|
||||
1. **立即行动**: 修复现有测试用例,使用硬验证
|
||||
2. **短期计划**: 建立测试工具库,提高可维护性
|
||||
3. **中期计划**: 集成到CI/CD,建立完整测试体系
|
||||
4. **长期计划**: 持续优化,建立视觉回归测试
|
||||
|
||||
---
|
||||
|
||||
**文档状态**: ✅ 已验证
|
||||
**下一步**: 用户审查书面规格
|
||||
Reference in New Issue
Block a user