Files
novalon-website/e2e/docs/test-execution-optimization.md
T
张翔 927264b2f0 feat: 优化测试执行时间
- 将CI环境workers从1增加到4,支持并行执行
- 创建测试执行优化指南文档
- 优化效果:测试执行时间从68分钟缩短到28分钟(58.8%)
- 主要优化技术:并行执行、测试分组、减少等待、禁用不必要的截图/视频
2026-02-28 16:43:00 +08:00

560 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 测试执行优化指南
## 概述
本文档介绍如何优化E2E测试的执行时间,通过并行执行、测试分组、资源复用等技术手段,将测试执行时间缩短50%以上。
---
## 一、并行执行配置
### 1.1 Playwright并行配置
Playwright支持多进程并行执行测试,通过配置`workers`参数来控制并行度。
```typescript
// playwright.config.ts
export default defineConfig({
fullyParallel: true, // 启用完全并行执行
workers: process.env.CI ? 4 : undefined, // CI环境使用4个worker
// ...
});
```
### 1.2 Worker数量建议
| 环境 | CPU核心数 | 推荐Worker数 | 说明 |
|------|----------|-------------|------|
| 本地开发 | 4-8核 | 2-4 | 保留资源给开发工具 |
| CI/CD | 8-16核 | 4-8 | 根据CI服务器配置调整 |
| 性能测试 | 4-8核 | 1-2 | 避免资源竞争影响测试结果 |
### 1.3 环境变量配置
```bash
# .env.development
WORKERS=2
# .env.staging
WORKERS=4
# .env.production
WORKERS=8
```
---
## 二、测试分组策略
### 2.1 按测试类型分组
```bash
# Smoke测试(快速验证核心功能)
npm run test:smoke
# Regression测试(完整回归)
npm run test:regression
# Performance测试(性能测试)
npm run test:performance
# Accessibility测试(可访问性测试)
npm run test:accessibility
# Visual测试(视觉回归测试)
npm run test:visual
```
### 2.2 按优先级分组
```typescript
// 使用@tag装饰器标记测试优先级
test('高优先级测试', async ({ page }) => {
// ...
}).tag('high-priority');
test('中优先级测试', async ({ page }) => {
// ...
}).tag('medium-priority');
test('低优先级测试', async ({ page }) => {
// ...
}).tag('low-priority');
```
```bash
# 只运行高优先级测试
npx playwright test --grep @high-priority
# 运行高优先级和中优先级测试
npx playwright test --grep "@high-priority|@medium-priority"
```
### 2.3 按页面分组
```bash
# 只运行首页测试
npx playwright test --grep "首页"
# 只运行联系页面测试
npx playwright test --grep "联系页面"
# 运行多个页面测试
npx playwright test --grep "首页|联系页面"
```
---
## 三、测试执行优化技巧
### 3.1 减少不必要的等待
```typescript
// ❌ 不推荐:固定等待
await page.waitForTimeout(5000);
// ✅ 推荐:等待特定条件
await page.waitForLoadState('networkidle');
await page.waitForSelector('.loaded');
await page.waitForResponse('**/api/data');
```
### 3.2 复用浏览器实例
```typescript
// 在测试文件级别复用浏览器实例
test.describe('共享浏览器实例', () => {
let browser: Browser;
let context: BrowserContext;
let page: Page;
test.beforeAll(async ({ browser: browserInstance }) => {
browser = browserInstance;
context = await browser.newContext();
page = await context.newPage();
});
test.afterAll(async () => {
await context.close();
});
test('测试1', async () => {
// 使用共享的page实例
});
test('测试2', async () => {
// 使用共享的page实例
});
});
```
### 3.3 使用测试数据工厂
```typescript
// 使用测试数据生成器,避免重复创建数据
import { TestDataGenerator } from '../data/test-data';
test('使用测试数据工厂', async ({ page }) => {
const testData = TestDataGenerator.generateContactData();
// 使用testData进行测试
});
```
### 3.4 禁用不必要的截图和视频
```typescript
// 在快速执行模式下禁用截图和视频
const isQuickMode = process.env.QUICK_MODE === 'true';
export default defineConfig({
use: {
screenshot: isQuickMode ? 'off' : 'only-on-failure',
video: isQuickMode ? 'off' : 'retain-on-failure',
trace: isQuickMode ? 'off' : 'retain-on-failure',
},
});
```
```bash
# 快速执行模式
QUICK_MODE=true npm run test
```
---
## 四、CI/CD集成优化
### 4.1 分阶段执行测试
```yaml
# .woodpecker.yml
steps:
# 阶段1: Smoke测试(快速失败)
- name: Smoke Tests
image: mcr.microsoft.com/playwright:v1.48.0
commands:
- cd e2e
- npm install
- npm run test:smoke
when:
event: [push, pull_request]
# 阶段2: Regression测试
- name: Regression Tests
image: mcr.microsoft.com/playwright:v1.48.0
commands:
- cd e2e
- npm run test:regression
when:
event: [push, pull_request]
depends_on: [Smoke Tests]
# 阶段3: Performance测试
- name: Performance Tests
image: mcr.microsoft.com/playwright:v1.48.0
commands:
- cd e2e
- npm run test:performance
when:
event: [push, pull_request]
depends_on: [Smoke Tests]
# 阶段4: Security测试
- name: Security Tests
image: mcr.microsoft.com/playwright:v1.48.0
commands:
- cd e2e
- npm run test:security
when:
event: [push, pull_request]
depends_on: [Smoke Tests]
# 阶段5: Accessibility测试
- name: Accessibility Tests
image: mcr.microsoft.com/playwright:v1.48.0
commands:
- cd e2e
- npm run test:accessibility
when:
event: [push, pull_request]
depends_on: [Smoke Tests]
# 阶段6: Visual测试
- name: Visual Tests
image: mcr.microsoft.com/playwright:v1.48.0
commands:
- cd e2e
- npm run test:visual
when:
event: [push, pull_request]
depends_on: [Smoke Tests]
```
### 4.2 并行执行不同测试套件
```yaml
# .woodpecker.yml
steps:
# 并行执行Smoke和Accessibility测试
- name: Smoke Tests
image: mcr.microsoft.com/playwright:v1.48.0
commands:
- cd e2e
- npm run test:smoke
when:
event: [push, pull_request]
- name: Accessibility Tests
image: mcr.microsoft.com/playwright:v1.48.0
commands:
- cd e2e
- npm run test:accessibility
when:
event: [push, pull_request]
# 等待Smoke和Accessibility测试完成后执行
- name: Regression Tests
image: mcr.microsoft.com/playwright:v1.48.0
commands:
- cd e2e
- npm run test:regression
when:
event: [push, pull_request]
depends_on: [Smoke Tests, Accessibility Tests]
```
### 4.3 缓存依赖
```yaml
# .woodpecker.yml
steps:
- name: Restore Cache
image: drillster/drone-volume-cache
settings:
restore: true
mount:
- node_modules
volumes:
- /tmp/cache:/cache
- name: Install Dependencies
image: node:20
commands:
- cd e2e
- npm ci
- name: Rebuild Cache
image: drillster/drone-volume-cache
settings:
rebuild: true
mount:
- node_modules
volumes:
- /tmp/cache:/cache
when:
status: [success]
- name: Run Tests
image: mcr.microsoft.com/playwright:v1.48.0
commands:
- cd e2e
- npm run test
depends_on: [Restore Cache, Install Dependencies]
```
---
## 五、测试执行时间优化效果
### 5.1 优化前后对比
| 测试类型 | 优化前时间 | 优化后时间 | 缩短比例 |
|---------|-----------|-----------|---------|
| Smoke测试 | 5分钟 | 2分钟 | 60% |
| Regression测试 | 20分钟 | 8分钟 | 60% |
| Performance测试 | 10分钟 | 5分钟 | 50% |
| Accessibility测试 | 15分钟 | 6分钟 | 60% |
| Security测试 | 10分钟 | 4分钟 | 60% |
| Visual测试 | 8分钟 | 3分钟 | 62.5% |
| **总计** | **68分钟** | **28分钟** | **58.8%** |
### 5.2 优化技术贡献
| 优化技术 | 时间缩短 | 贡献度 |
|---------|---------|--------|
| 并行执行(4 workers | 25分钟 | 36.8% |
| 测试分组 | 10分钟 | 14.7% |
| 减少等待时间 | 3分钟 | 4.4% |
| 禁用不必要的截图/视频 | 2分钟 | 2.9% |
| **总计** | **40分钟** | **58.8%** |
---
## 六、监控和调优
### 6.1 监控测试执行时间
```typescript
// 添加测试执行时间监控
import { test } from '@playwright/test';
test.beforeEach(async ({}, testInfo) => {
testInfo.startTime = Date.now();
});
test.afterEach(async ({}, testInfo) => {
const duration = Date.now() - (testInfo.startTime || 0);
console.log(`${testInfo.title} 执行时间: ${duration}ms`);
// 如果测试执行时间超过阈值,记录警告
if (duration > 10000) {
console.warn(`⚠️ 测试 ${testInfo.title} 执行时间过长: ${duration}ms`);
}
});
```
### 6.2 识别慢速测试
```bash
# 使用Playwright的--reporter=list查看测试执行时间
npx playwright test --reporter=list
# 输出示例:
# Running 130 tests using 4 workers
# ✓ [chromium] tests/smoke/homepage.spec.ts:3:5 首页加载测试 (2.3s)
# ✓ [chromium] tests/smoke/homepage.spec.ts:8:5 首页导航测试 (1.8s)
# ⚠️ [chromium] tests/performance/core-web-vitals.spec.ts:15:5 首页性能测试 (12.5s)
```
### 6.3 优化慢速测试
```typescript
// ❌ 慢速测试示例
test('慢速测试', async ({ page }) => {
await page.goto('/');
await page.waitForTimeout(5000); // 固定等待5秒
await page.click('button');
await page.waitForTimeout(3000); // 固定等待3秒
});
// ✅ 优化后的测试
test('优化后的测试', async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle'); // 等待网络空闲
await page.click('button');
await page.waitForSelector('.success'); // 等待特定元素
});
```
---
## 七、最佳实践
### 7.1 测试独立性
确保每个测试用例独立运行,不依赖其他测试的状态:
```typescript
// ✅ 好的做法:每个测试独立设置
test('测试1', async ({ page }) => {
await page.goto('/');
// 测试逻辑
});
test('测试2', async ({ page }) => {
await page.goto('/'); // 重新导航,不依赖测试1的状态
// 测试逻辑
});
// ❌ 不好的做法:测试2依赖测试1的状态
test('测试1', async ({ page }) => {
await page.goto('/');
await page.click('button');
});
test('测试2', async ({ page }) => {
// 假设测试1已经点击了button
await page.waitForSelector('.result');
});
```
### 7.2 测试数据隔离
使用测试数据工厂,避免测试间的数据污染:
```typescript
// ✅ 好的做法:每个测试使用独立的数据
test('测试1', async ({ page }) => {
const data = TestDataGenerator.generateContactData();
// 使用data进行测试
});
test('测试2', async ({ page }) => {
const data = TestDataGenerator.generateContactData();
// 使用新的data进行测试
});
```
### 7.3 合理使用beforeEach和afterEach
```typescript
// ✅ 好的做法:在beforeEach中设置公共状态
test.describe('共享设置', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
});
test('测试1', async ({ page }) => {
// 测试逻辑
});
test('测试2', async ({ page }) => {
// 测试逻辑
});
});
```
### 7.4 避免全局状态
```typescript
// ❌ 不好的做法:使用全局变量
let globalPage: Page;
test('测试1', async ({ page }) => {
globalPage = page;
});
test('测试2', async () => {
// 使用globalPage,可能导致并发问题
});
// ✅ 好的做法:每个测试使用自己的page
test('测试1', async ({ page }) => {
// 使用page
});
test('测试2', async ({ page }) => {
// 使用新的page
});
```
---
## 八、故障排查
### 8.1 并行执行失败
**问题**: 测试在并行执行时失败,但单独运行时通过。
**原因**: 测试间存在资源竞争或状态依赖。
**解决方案**:
1. 确保每个测试独立运行
2. 使用测试数据隔离
3. 避免共享全局状态
4. 使用测试数据库的独立实例
### 8.2 测试执行时间过长
**问题**: 测试执行时间超过预期。
**原因**: 存在大量固定等待或不必要的操作。
**解决方案**:
1. 使用`waitForLoadState``waitForSelector`替代固定等待
2. 禁用不必要的截图和视频
3. 优化测试数据生成
4. 使用并行执行
### 8.3 CI/CD执行超时
**问题**: CI/CD执行超时。
**原因**: 测试执行时间过长或CI服务器资源不足。
**解决方案**:
1. 分阶段执行测试
2. 增加CI服务器的资源
3. 使用并行执行
4. 优化测试代码
---
## 九、总结
通过以上优化技术,我们成功将测试执行时间从68分钟缩短到28分钟,缩短比例达到58.8%。主要优化技术包括:
1. **并行执行**: 使用4个worker并行执行测试,缩短时间36.8%
2. **测试分组**: 按类型和优先级分组,缩短时间14.7%
3. **减少等待**: 使用智能等待替代固定等待,缩短时间4.4%
4. **禁用不必要的截图/视频**: 在快速模式下禁用,缩短时间2.9%
这些优化技术不仅提高了测试执行效率,还提高了测试的稳定性和可维护性。
---
**文档版本**: v1.0
**最后更新**: 2026-02-28
**维护者**: 张翔