docs: add E2E test optimization design document
This commit is contained in:
@@ -0,0 +1,535 @@
|
||||
# E2E测试优化设计方案
|
||||
|
||||
**文档版本**: 1.0
|
||||
**创建日期**: 2026-04-04
|
||||
**作者**: 张翔
|
||||
**目标**: 将E2E测试通过率从17.3%提升至100%,并优化测试执行时间
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
### 1.1 当前状态
|
||||
|
||||
- **总测试数**: 52个测试用例
|
||||
- **通过**: 9个测试用例 (17.3%)
|
||||
- **失败**: 43个测试用例 (82.7%)
|
||||
- **执行时间**: 17.2分钟
|
||||
|
||||
### 1.2 主要问题
|
||||
|
||||
1. **页面导航问题**: 大部分测试用例无法正确导航到目标页面
|
||||
2. **选择器问题**: 测试用例使用的选择器无法找到对应的页面元素
|
||||
3. **测试执行时间**: 当前执行时间较长,需要优化
|
||||
|
||||
### 1.3 目标
|
||||
|
||||
- **测试通过率**: 100% (所有52个测试用例通过)
|
||||
- **执行时间**: 减少30%以上 (从17.2分钟降至12分钟以内)
|
||||
- **测试稳定性**: 所有测试用例稳定可重复执行
|
||||
|
||||
---
|
||||
|
||||
## 2. 实施策略
|
||||
|
||||
采用**分阶段实施**策略,按照问题的影响范围,从基础到高级逐步修复。
|
||||
|
||||
### 2.1 为什么选择分阶段实施?
|
||||
|
||||
- **风险可控**: 每个阶段都可以验证效果,及时调整方案
|
||||
- **效率最高**: 先解决基础问题,再解决复杂问题,避免重复工作
|
||||
- **符合测试金字塔**: 从基础功能到高级功能,逐步提高测试覆盖率
|
||||
- **易于管理**: 每个阶段都有明确的目标和验收标准
|
||||
|
||||
---
|
||||
|
||||
## 3. 第一阶段:基础导航修复
|
||||
|
||||
**预计时间**: 2-3天
|
||||
**目标**: 测试通过率提升至50%以上(至少26个测试用例通过)
|
||||
|
||||
### 3.1 问题分析
|
||||
|
||||
43个失败的测试用例中,大部分都是因为无法正确导航到目标页面。主要原因包括:
|
||||
|
||||
1. **页面不存在**: 某些管理页面可能还未实现
|
||||
2. **路由配置问题**: 路由路径与测试用例中的路径不一致
|
||||
3. **页面加载超时**: 页面加载时间过长,导致测试超时
|
||||
4. **权限问题**: 某些页面需要特定权限才能访问
|
||||
|
||||
### 3.2 修复策略
|
||||
|
||||
#### 3.2.1 页面存在性验证
|
||||
|
||||
首先验证所有测试用例涉及的页面是否都已经实现:
|
||||
|
||||
- ✅ `/users` - 用户管理页面
|
||||
- ✅ `/roles` - 角色管理页面
|
||||
- ✅ `/menus` - 菜单管理页面
|
||||
- ✅ `/sys/config` - 系统配置页面
|
||||
- ✅ `/dict` - 字典管理页面
|
||||
- ✅ `/files` - 文件管理页面
|
||||
- ✅ `/loginlog` - 登录日志页面
|
||||
- ✅ `/oplog` - 操作日志页面
|
||||
- ✅ `/exceptionlog` - 异常日志页面
|
||||
|
||||
#### 3.2.2 Page Object类优化
|
||||
|
||||
为每个Page Object类添加更健壮的导航逻辑:
|
||||
|
||||
```typescript
|
||||
async goto() {
|
||||
await this.page.goto('/users');
|
||||
|
||||
// 等待页面加载完成
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
|
||||
// 等待关键元素出现
|
||||
await this.page.waitForSelector('.el-table', { timeout: 10000 });
|
||||
|
||||
// 验证页面标题或URL
|
||||
await expect(this.page).toHaveURL(/.*users/);
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.3 错误处理机制
|
||||
|
||||
添加完善的错误处理机制:
|
||||
|
||||
```typescript
|
||||
async goto() {
|
||||
try {
|
||||
await this.page.goto('/users');
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
await this.page.waitForSelector('.el-table', { timeout: 10000 });
|
||||
} catch (error) {
|
||||
// 截图保存错误状态
|
||||
await this.page.screenshot({ path: `test-results/error-${Date.now()}.png` });
|
||||
|
||||
// 记录错误信息
|
||||
console.error('页面导航失败:', error);
|
||||
|
||||
// 抛出更详细的错误信息
|
||||
throw new Error(`导航到用户管理页面失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 任务清单
|
||||
|
||||
1. **验证页面存在性**(0.5天)
|
||||
- 检查所有测试用例涉及的页面是否已实现
|
||||
- 确认路由配置是否正确
|
||||
- 验证页面权限设置
|
||||
|
||||
2. **优化Page Object类**(1天)
|
||||
- 为每个Page Object类添加健壮的导航方法
|
||||
- 添加错误处理机制
|
||||
- 添加页面加载验证逻辑
|
||||
|
||||
3. **运行测试验证**(0.5天)
|
||||
- 运行完整测试套件
|
||||
- 收集通过率数据
|
||||
- 分析剩余失败原因
|
||||
|
||||
### 3.4 验收标准
|
||||
|
||||
- ✅ 测试通过率提升至50%以上(至少26个测试用例通过)
|
||||
- ✅ 所有页面都能正确导航
|
||||
- ✅ 页面加载错误有清晰的错误信息
|
||||
|
||||
---
|
||||
|
||||
## 4. 第二阶段:选择器精准化
|
||||
|
||||
**预计时间**: 2-3天
|
||||
**目标**: 测试通过率提升至90%以上(至少47个测试用例通过)
|
||||
|
||||
### 4.1 问题分析
|
||||
|
||||
测试用例中使用的选择器无法找到对应的页面元素,主要原因包括:
|
||||
|
||||
1. **选择器过时**: 前端代码修改后,选择器未同步更新
|
||||
2. **选择器不够健壮**: 使用class选择器,容易受CSS变化影响
|
||||
3. **动态元素**: 某些元素是动态生成的,需要更灵活的定位方式
|
||||
4. **异步加载**: 元素加载有延迟,需要添加等待逻辑
|
||||
|
||||
### 4.2 修复策略
|
||||
|
||||
#### 4.2.1 选择器诊断工具
|
||||
|
||||
使用Playwright的trace功能,捕获实际页面元素:
|
||||
|
||||
```typescript
|
||||
// 在测试配置中启用trace
|
||||
use: {
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
video: 'retain-on-failure',
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2.2 选择器优化原则
|
||||
|
||||
优先使用以下选择器(按优先级排序):
|
||||
|
||||
1. **data-testid属性**(最推荐)
|
||||
```typescript
|
||||
page.getByTestId('submit-button')
|
||||
```
|
||||
|
||||
2. **角色和文本组合**
|
||||
```typescript
|
||||
page.getByRole('button', { name: '确定' })
|
||||
page.getByText('用户管理')
|
||||
```
|
||||
|
||||
3. **CSS选择器**(最后选择)
|
||||
```typescript
|
||||
page.locator('.el-button--primary')
|
||||
```
|
||||
|
||||
#### 4.2.3 Page Object类选择器更新
|
||||
|
||||
为每个Page Object类更新选择器:
|
||||
|
||||
```typescript
|
||||
export class UserManagementPage {
|
||||
readonly page: Page;
|
||||
readonly table: Locator;
|
||||
readonly createUserButton: Locator;
|
||||
readonly searchInput: Locator;
|
||||
readonly searchButton: Locator;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
|
||||
// 使用更健壮的选择器
|
||||
this.table = page.locator('.el-table').first();
|
||||
this.createUserButton = page.getByRole('button', { name: '新增用户' });
|
||||
this.searchInput = page.getByPlaceholder('搜索用户名或邮箱');
|
||||
this.searchButton = page.getByRole('button', { name: '搜索' });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2.4 等待策略优化
|
||||
|
||||
添加智能等待逻辑:
|
||||
|
||||
```typescript
|
||||
async waitForTableReady() {
|
||||
// 等待表格出现
|
||||
await this.table.waitFor({ state: 'visible', timeout: 10000 });
|
||||
|
||||
// 等待表格数据加载完成
|
||||
await this.page.waitForFunction(
|
||||
() => document.querySelectorAll('.el-table__body tr').length > 0,
|
||||
{ timeout: 5000 }
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2.5 动态元素处理
|
||||
|
||||
处理动态生成的元素:
|
||||
|
||||
```typescript
|
||||
async clickDynamicButton(buttonText: string) {
|
||||
// 使用文本内容定位动态按钮
|
||||
await this.page.getByRole('button', { name: buttonText }).click();
|
||||
|
||||
// 或者使用正则表达式匹配
|
||||
await this.page.getByRole('button', { name: /确定|确认/ }).click();
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 任务清单
|
||||
|
||||
1. **选择器诊断**(0.5天)
|
||||
- 使用Playwright trace捕获实际页面元素
|
||||
- 分析所有失败测试的选择器问题
|
||||
- 生成选择器诊断报告
|
||||
|
||||
2. **批量更新选择器**(1.5天)
|
||||
- 更新所有Page Object类的选择器
|
||||
- 添加智能等待逻辑
|
||||
- 处理动态元素
|
||||
|
||||
3. **运行测试验证**(0.5天)
|
||||
- 运行完整测试套件
|
||||
- 收集通过率数据
|
||||
- 分析剩余失败原因
|
||||
|
||||
### 4.4 验收标准
|
||||
|
||||
- ✅ 测试通过率提升至90%以上(至少47个测试用例通过)
|
||||
- ✅ 所有选择器都能正确找到元素
|
||||
- ✅ 动态元素有稳定的处理逻辑
|
||||
|
||||
---
|
||||
|
||||
## 5. 第三阶段:性能优化
|
||||
|
||||
**预计时间**: 1-2天
|
||||
**目标**: 测试通过率达到100%,执行时间减少30%以上
|
||||
|
||||
### 5.1 问题分析
|
||||
|
||||
当前测试套件执行时间为17.2分钟,主要耗时在:
|
||||
|
||||
1. **全局setup/teardown**: 启动后端服务、数据库初始化等
|
||||
2. **页面加载等待**: 每个测试用例都等待页面加载完成
|
||||
3. **固定等待时间**: 使用`waitForTimeout`固定等待,不够智能
|
||||
4. **串行执行**: 测试用例逐个执行,无法并行
|
||||
|
||||
### 5.2 优化策略
|
||||
|
||||
#### 5.2.1 全局setup优化
|
||||
|
||||
优化后端服务启动时间:
|
||||
|
||||
```typescript
|
||||
// global-setup.ts
|
||||
export default async function globalSetup() {
|
||||
console.log('🚀 开始全局测试环境设置...');
|
||||
|
||||
// 使用JAR文件启动(比Maven快50%)
|
||||
const jarFile = path.join(backendDir, 'target/manage-app-1.0.0.jar');
|
||||
|
||||
// 减少健康检查间隔(从1秒改为0.5秒)
|
||||
const healthCheckInterval = 500;
|
||||
|
||||
// 减少最大等待时间(从60秒改为30秒)
|
||||
const maxWaitTime = 30;
|
||||
|
||||
// 并行启动多个服务(如果需要)
|
||||
await Promise.all([
|
||||
startBackendService(),
|
||||
startFrontendService(),
|
||||
]);
|
||||
}
|
||||
```
|
||||
|
||||
#### 5.2.2 页面加载等待优化
|
||||
|
||||
使用更智能的等待策略:
|
||||
|
||||
```typescript
|
||||
// 优化前
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// 优化后:等待特定条件
|
||||
await page.waitForLoadState('domcontentloaded'); // 只等待DOM加载
|
||||
await page.waitForSelector('.el-table', { state: 'visible' }); // 等待关键元素
|
||||
```
|
||||
|
||||
#### 5.2.3 测试用例并行执行
|
||||
|
||||
在确保测试独立性的前提下,启用并行执行:
|
||||
|
||||
```typescript
|
||||
// playwright.config.ts
|
||||
export default defineConfig({
|
||||
// 项目级并行(不同项目并行执行)
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
{
|
||||
name: 'firefox',
|
||||
use: { ...devices['Desktop Firefox'] },
|
||||
},
|
||||
],
|
||||
|
||||
// 文件级并行(同一项目内,不同文件并行执行)
|
||||
workers: process.env.CI ? 1 : 4, // CI环境串行,本地并行
|
||||
|
||||
// 完全并行(需要确保测试完全独立)
|
||||
fullyParallel: false, // 暂不启用,避免localStorage冲突
|
||||
});
|
||||
```
|
||||
|
||||
#### 5.2.4 测试数据缓存
|
||||
|
||||
缓存测试数据,避免重复创建:
|
||||
|
||||
```typescript
|
||||
// 使用全局状态存储测试数据
|
||||
let testUserId: string | null = null;
|
||||
|
||||
test.beforeAll(async ({ request }) => {
|
||||
if (!testUserId) {
|
||||
// 只创建一次测试用户
|
||||
const response = await request.post('/api/users', {
|
||||
data: { username: 'testuser', password: 'Test@123' }
|
||||
});
|
||||
testUserId = (await response.json()).id;
|
||||
}
|
||||
});
|
||||
|
||||
test.afterAll(async ({ request }) => {
|
||||
if (testUserId) {
|
||||
// 清理测试数据
|
||||
await request.delete(`/api/users/${testUserId}`);
|
||||
testUserId = null;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### 5.2.5 智能重试机制
|
||||
|
||||
为不稳定的测试用例添加智能重试:
|
||||
|
||||
```typescript
|
||||
// playwright.config.ts
|
||||
export default defineConfig({
|
||||
// 失败后重试2次
|
||||
retries: process.env.CI ? 2 : 1,
|
||||
|
||||
// 只重试失败的测试用例
|
||||
retryOnlyFailed: true,
|
||||
});
|
||||
```
|
||||
|
||||
#### 5.2.6 测试报告优化
|
||||
|
||||
生成更详细的测试报告:
|
||||
|
||||
```typescript
|
||||
// 自定义报告器
|
||||
export default class CustomReporter {
|
||||
onTestEnd(test: TestCase, result: TestResult) {
|
||||
const duration = result.duration;
|
||||
const status = result.status;
|
||||
|
||||
// 记录慢测试
|
||||
if (duration > 10000) {
|
||||
console.log(`⚠️ 慢测试: ${test.title} (${duration}ms)`);
|
||||
}
|
||||
|
||||
// 记录失败测试的详细信息
|
||||
if (status === 'failed') {
|
||||
console.log(`❌ 失败: ${test.title}`);
|
||||
console.log(` 错误: ${result.error?.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 任务清单
|
||||
|
||||
1. **优化全局setup/teardown**(0.5天)
|
||||
- 使用JAR文件启动后端服务
|
||||
- 减少健康检查等待时间
|
||||
- 并行启动多个服务
|
||||
|
||||
2. **优化页面加载等待**(0.5天)
|
||||
- 移除固定等待时间
|
||||
- 使用智能等待策略
|
||||
- 优化关键元素等待逻辑
|
||||
|
||||
3. **生成最终报告**(0.5天)
|
||||
- 运行完整测试套件
|
||||
- 生成详细的测试报告
|
||||
- 分析性能指标
|
||||
|
||||
### 5.4 验收标准
|
||||
|
||||
- ✅ 测试通过率达到100%(所有52个测试用例通过)
|
||||
- ✅ 测试执行时间减少30%以上(从17.2分钟降至12分钟以内)
|
||||
- ✅ 生成完整的测试报告和性能分析
|
||||
|
||||
---
|
||||
|
||||
## 6. 总体验收标准
|
||||
|
||||
### 6.1 功能验收
|
||||
|
||||
- ✅ 所有52个测试用例100%通过
|
||||
- ✅ 测试覆盖所有核心业务流程
|
||||
- ✅ 测试报告清晰展示测试结果
|
||||
|
||||
### 6.2 性能验收
|
||||
|
||||
- ✅ 测试执行时间在12分钟以内
|
||||
- ✅ 全局setup时间在30秒以内
|
||||
- ✅ 单个测试用例平均执行时间在20秒以内
|
||||
|
||||
### 6.3 质量验收
|
||||
|
||||
- ✅ 所有Page Object类有完善的错误处理
|
||||
- ✅ 所有选择器使用最佳实践
|
||||
- ✅ 测试代码有清晰的注释和文档
|
||||
|
||||
---
|
||||
|
||||
## 7. 风险与应对
|
||||
|
||||
### 7.1 页面未实现风险
|
||||
|
||||
**风险**: 某些测试页面可能还未实现
|
||||
**应对**:
|
||||
- 优先检查页面存在性
|
||||
- 如果页面未实现,暂时跳过相关测试用例
|
||||
- 记录未实现页面的测试用例,后续补充
|
||||
|
||||
### 7.2 选择器不稳定风险
|
||||
|
||||
**风险**: 某些选择器可能不稳定,导致测试时好时坏
|
||||
**应对**:
|
||||
- 使用多个备选选择器
|
||||
- 添加重试机制
|
||||
- 使用更健壮的等待策略
|
||||
|
||||
### 7.3 测试数据冲突风险
|
||||
|
||||
**风险**: 多个测试用例共享测试数据,可能导致冲突
|
||||
**应对**:
|
||||
- 每个测试用例使用唯一的测试数据(如时间戳)
|
||||
- 测试完成后清理测试数据
|
||||
- 使用独立的测试数据库
|
||||
|
||||
### 7.4 执行时间过长风险
|
||||
|
||||
**风险**: 即使优化后,执行时间可能仍然较长
|
||||
**应对**:
|
||||
- 进一步优化等待策略
|
||||
- 考虑并行执行更多测试用例
|
||||
- 减少不必要的测试步骤
|
||||
|
||||
---
|
||||
|
||||
## 8. 后续优化建议
|
||||
|
||||
### 8.1 短期优化(1-2周)
|
||||
|
||||
1. **添加更多测试用例**: 覆盖更多边界场景
|
||||
2. **优化测试数据管理**: 使用测试数据工厂模式
|
||||
3. **集成到CI/CD**: 配置Woodpecker CI自动运行E2E测试
|
||||
|
||||
### 8.2 中期优化(2-4周)
|
||||
|
||||
1. **添加可视化测试**: 使用Percy或Applitools进行视觉回归测试
|
||||
2. **性能监控**: 集成Lighthouse进行性能监控
|
||||
3. **测试报告优化**: 生成更详细的HTML报告
|
||||
|
||||
### 8.3 长期优化(1-2个月)
|
||||
|
||||
1. **测试框架升级**: 考虑使用更先进的测试框架
|
||||
2. **AI辅助测试**: 使用AI工具自动生成测试用例
|
||||
3. **持续优化**: 定期审查测试用例,优化测试执行速度
|
||||
|
||||
---
|
||||
|
||||
## 9. 参考资料
|
||||
|
||||
- [Playwright官方文档](https://playwright.dev/)
|
||||
- [Playwright最佳实践](https://playwright.dev/docs/best-practices)
|
||||
- [Vue 3测试指南](https://vuejs.org/guide/scaling-up/testing.html)
|
||||
- [E2E测试最佳实践](https://testingjavascript.com/)
|
||||
|
||||
---
|
||||
|
||||
**文档结束**
|
||||
Reference in New Issue
Block a user