Files
novalon-website/docs/plans/2026-03-05-mobile-testing-enhancement-design.md
T

1384 lines
39 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.
# 移动端测试完善方案设计文档
**创建日期**: 2026-03-05
**项目**: Novalon Website E2E Testing Enhancement
**目标**: 建立全面完善的移动端测试体系,覆盖性能、兼容性、手势交互、PWA 功能等方面
---
## 1. 项目背景
### 1.1 当前现状
项目已建立基于 Playwright 的 E2E 测试框架,具备基础的移动端测试能力:
- ✅ 基本的移动端 UI 交互测试(菜单、导航、表单)
- ✅ 多设备尺寸配置(iPhone、Android、Tablet
- ✅ 基础的触摸交互测试
- ✅ 响应式布局测试
### 1.2 存在的问题
当前移动端测试体系存在以下不足:
- 缺乏系统的性能测试,无法验证移动端网络条件下的加载性能
- 兼容性测试覆盖不全面,缺乏跨设备、跨浏览器的深度验证
- 手势交互测试仅覆盖基本操作,缺乏复杂手势测试
- 缺少 PWA 功能测试,无法验证离线缓存、安装等功能
- 测试报告缺乏移动端特定的分析和可视化
### 1.3 项目目标
建立企业级移动端测试体系,实现:
- 🎯 全面的性能测试覆盖,包括 Core Web Vitals 和网络性能
- 🎯 完整的兼容性测试矩阵,支持多设备、多浏览器验证
- 🎯 丰富的手势交互测试,覆盖所有常见移动端手势
- 🎯 完整的 PWA 功能测试,验证移动端特性
- 🎯 智能的测试报告系统,提供移动端专属的分析和可视化
---
## 2. 整体架构设计
采用分层架构设计,确保系统的可扩展性和可维护性:
```
┌─────────────────────────────────────────────────────────────┐
│ 测试执行层 │
│ - 测试配置扩展 - 并行测试策略 - 测试报告系统 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 测试用例层 │
│ - 性能测试套件 - 兼容性测试套件 - 手势交互测试套件 │
│ - PWA 功能测试套件 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 测试工具层 │
│ - 手势模拟器 - 网络环境模拟器 - 性能监控器 │
│ - 设备兼容性验证工具 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 测试数据层 │
│ - 移动端测试数据生成器 - 设备配置数据 - 网络配置数据 │
│ - 性能基准数据 - 测试数据仓库 │
└─────────────────────────────────────────────────────────────┘
```
### 2.1 测试数据层
**职责**: 提供移动端测试所需的所有数据支持
**核心组件**:
1. **移动端测试数据生成器**
- 扩展现有的 `TestDataGenerator`
- 增加设备特定的测试数据(User Agent、触摸事件数据)
- 支持不同网络条件下的测试数据
2. **设备配置数据**
- 扩展 `devices.ts`,增加更多真实设备配置
- 支持 iPhone 13/14/15 系列
- 支持 Samsung Galaxy、Google Pixel 系列
- 支持 iPad Air/Pro 系列
3. **网络配置数据**
- 预设网络配置(2G、3G、4G、WiFi)
- 自定义网络配置(延迟、丢包率、带宽)
- 网络切换场景配置
4. **性能基准数据**
- Core Web Vitals 基准值
- 不同设备类型的性能阈值
- 性能回归检测基准
5. **测试数据仓库**
- 集中管理所有测试数据
- 支持数据版本控制
- 提供数据查询和复用机制
### 2.2 测试工具层
**职责**: 提供移动端测试所需的专用工具和功能
**核心组件**:
1. **手势模拟器 (GestureSimulator)**
- 基于 Playwright Touch API 构建
- 支持的手势类型:
- 单指滑动(水平、垂直,支持速度和距离控制)
- 双指捏合缩放(支持缩放比例控制)
- 长按操作(支持自定义时长)
- 双击操作(支持自定义间隔时间)
- 拖拽操作(支持方向和距离控制)
- 提供统一的 API 接口
- 支持链式调用和参数化配置
2. **网络环境模拟器 (NetworkSimulator)**
- 利用 Chrome DevTools Protocol 实现
- 支持的网络场景:
- 弱网环境(2G、3G、4G 不同信号强度)
- 离线模式(完全断网)
- 网络切换(在线→离线、弱网→强网)
- 网络延迟和丢包模拟
- 提供预设配置和自定义配置
- 支持动态切换网络环境
3. **性能监控器 (PerformanceMonitor)**
- 集成 Lighthouse API
- 监控的 Core Web Vitals 指标:
- First Contentful Paint (FCP)
- Largest Contentful Paint (LCP)
- Cumulative Layout Shift (CLS)
- First Input Delay (FID)
- Time to Interactive (TTI)
- 建立移动端性能基准
- 支持性能回归检测
- 生成详细性能分析报告
4. **设备兼容性验证工具 (CompatibilityValidator)**
- 跨设备布局验证
- 功能兼容性检查
- 渲染一致性对比
- 系统版本降级测试
### 2.3 测试用例层
**职责**: 组织和管理所有移动端测试用例
**核心组件**:
1. **性能测试套件 (PerformanceTestSuite)**
- 首屏加载性能测试
- 页面交互响应测试
- 资源加载优化测试
- 内存泄漏检测测试
- 每个测试用例设置明确的性能阈值
2. **兼容性测试套件 (CompatibilityTestSuite)**
- 不同屏幕尺寸的布局适配测试
- 不同操作系统的功能兼容性测试
- 不同浏览器的渲染一致性测试
- 不同系统版本的功能降级测试
- 采用参数化测试方式
3. **手势交互测试套件 (GestureTestSuite)**
- 单指滑动测试(页面滚动、轮播图切换)
- 双指捏合缩放测试(图片查看、地图缩放)
- 长按操作测试(上下文菜单、多选操作)
- 双击操作测试(图片放大、点赞功能)
- 拖拽操作测试(卡片排序、元素移动)
4. **PWA 功能测试套件 (PWATestSuite)**
- Service Worker 注册测试
- 离线缓存功能测试
- 应用安装测试
- 推送通知测试
- 应用更新测试
### 2.4 测试执行层
**职责**: 管理测试的执行、调度和报告
**核心组件**:
1. **测试配置扩展**
- 扩展现有 Playwright 配置
- 增加移动设备项目配置
- 增加网络环境项目配置
- 增加性能测试专用项目配置
- 支持动态设备选择和网络条件切换
2. **并行测试策略**
- 设备级并行(不同设备上的测试用例同时执行)
- 网络条件级并行(同一设备在不同网络条件下并行测试)
- 智能测试调度(根据依赖关系和资源使用优化执行顺序)
- 显著缩短测试执行时间
3. **测试报告系统**
- 移动端测试概览(通过率、失败率、执行时间)
- 设备兼容性报告(每个设备上的测试结果对比)
- 性能分析报告(Core Web Vitals 趋势、性能回归检测)
- 网络环境影响报告(不同网络条件下的性能对比)
- 支持多种输出格式(HTML、JSON、JUnit
---
## 3. 详细设计
### 3.1 测试数据层详细设计
#### 3.1.1 移动端测试数据生成器
**文件位置**: `e2e/src/utils/MobileTestDataGenerator.ts`
**核心功能**:
```typescript
export class MobileTestDataGenerator {
// 生成设备特定的 User Agent
static generateUserAgent(device: string): string;
// 生成触摸事件数据
static generateTouchEvent(type: 'tap' | 'swipe' | 'pinch' | 'longPress'): TouchEventData;
// 生成网络配置数据
static generateNetworkConfig(type: '2G' | '3G' | '4G' | 'WiFi' | 'offline'): NetworkConfig;
// 生成性能基准数据
static generatePerformanceBaseline(device: string): PerformanceBaseline;
// 生成手势测试数据
static generateGestureData(gesture: GestureType): GestureData;
}
```
#### 3.1.2 扩展设备配置
**文件位置**: `e2e/src/utils/devices.ts`
**新增设备配置**:
```typescript
export const devices: Record<string, DeviceConfig> = {
// 现有设备配置...
// 新增 iPhone 系列
'iphone-13-pro': {
name: 'iPhone 13 Pro',
viewport: { width: 390, height: 844 },
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)...',
isMobile: true,
deviceScaleFactor: 3,
},
'iphone-14-pro': {
name: 'iPhone 14 Pro',
viewport: { width: 393, height: 852 },
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)...',
isMobile: true,
deviceScaleFactor: 3,
},
'iphone-15-pro': {
name: 'iPhone 15 Pro',
viewport: { width: 393, height: 852 },
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)...',
isMobile: true,
deviceScaleFactor: 3,
},
// 新增 Android 系列
'pixel-7': {
name: 'Google Pixel 7',
viewport: { width: 412, height: 915 },
userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel 7)...',
isMobile: true,
deviceScaleFactor: 2.625,
},
'samsung-galaxy-s23': {
name: 'Samsung Galaxy S23',
viewport: { width: 360, height: 780 },
userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-S911B)...',
isMobile: true,
deviceScaleFactor: 3,
},
// 新增 iPad 系列
'ipad-air': {
name: 'iPad Air',
viewport: { width: 820, height: 1180 },
userAgent: 'Mozilla/5.0 (iPad; CPU OS 16_0 like Mac OS X)...',
isMobile: true,
deviceScaleFactor: 2,
},
'ipad-pro-12-9': {
name: 'iPad Pro 12.9"',
viewport: { width: 1024, height: 1366 },
userAgent: 'Mozilla/5.0 (iPad; CPU OS 16_0 like Mac OS X)...',
isMobile: true,
deviceScaleFactor: 2,
},
};
```
#### 3.1.3 网络配置数据
**文件位置**: `e2e/src/config/network-configs.ts`
**网络配置示例**:
```typescript
export const networkConfigs: Record<string, NetworkConfig> = {
'2g-slow': {
name: '2G Slow',
offline: false,
downloadThroughput: 250 * 1024, // 250 Kbps
uploadThroughput: 50 * 1024, // 50 Kbps
latency: 2000, // 2000ms
},
'3g-fast': {
name: '3G Fast',
offline: false,
downloadThroughput: 1.6 * 1024 * 1024, // 1.6 Mbps
uploadThroughput: 750 * 1024, // 750 Kbps
latency: 100, // 100ms
},
'4g-lte': {
name: '4G LTE',
offline: false,
downloadThroughput: 4 * 1024 * 1024, // 4 Mbps
uploadThroughput: 3 * 1024 * 1024, // 3 Mbps
latency: 20, // 20ms
},
'wifi-fast': {
name: 'WiFi Fast',
offline: false,
downloadThroughput: 30 * 1024 * 1024, // 30 Mbps
uploadThroughput: 15 * 1024 * 1024, // 15 Mbps
latency: 2, // 2ms
},
'offline': {
name: 'Offline',
offline: true,
},
};
```
### 3.2 测试工具层详细设计
#### 3.2.1 手势模拟器
**文件位置**: `e2e/src/utils/GestureSimulator.ts`
**核心功能**:
```typescript
export class GestureSimulator {
constructor(private page: Page) {}
// 单指滑动
async swipe(options: SwipeOptions): Promise<void>;
// 双指捏合缩放
async pinch(options: PinchOptions): Promise<void>;
// 长按
async longPress(element: Locator, duration: number): Promise<void>;
// 双击
async doubleTap(element: Locator): Promise<void>;
// 拖拽
async drag(options: DragOptions): Promise<void>;
// 快速滑动(用于刷新)
async quickSwipeDown(): Promise<void>;
// 慢速滑动(用于浏览)
async slowSwipeUp(): Promise<void>;
}
```
**使用示例**:
```typescript
const gestureSimulator = new GestureSimulator(page);
// 向上滑动页面
await gestureSimulator.swipe({
startX: 200,
startY: 600,
endX: 200,
endY: 200,
duration: 500,
});
// 双指缩放图片
await gestureSimulator.pinch({
centerX: 200,
centerY: 300,
startDistance: 100,
endDistance: 50,
duration: 300,
});
// 长按元素
await gestureSimulator.longPress(page.locator('.card'), 1000);
```
#### 3.2.2 网络环境模拟器
**文件位置**: `e2e/src/utils/NetworkSimulator.ts`
**核心功能**:
```typescript
export class NetworkSimulator {
constructor(private context: BrowserContext) {}
// 设置网络条件
async setNetworkCondition(config: NetworkConfig): Promise<void>;
// 切换到离线模式
async goOffline(): Promise<void>;
// 切换到在线模式
async goOnline(): Promise<void>;
// 模拟网络切换
async simulateNetworkSwitch(fromConfig: NetworkConfig, toConfig: NetworkConfig): Promise<void>;
// 重置网络条件
async resetNetworkCondition(): Promise<void>;
// 监控网络请求
async monitorNetworkRequests(): Promise<NetworkRequest[]>;
}
```
**使用示例**:
```typescript
const networkSimulator = new NetworkSimulator(context);
// 设置 3G 网络条件
await networkSimulator.setNetworkCondition(networkConfigs['3g-fast']);
// 测试弱网环境下的页面加载
await page.goto('/');
// 切换到离线模式
await networkSimulator.goOffline();
// 验证离线缓存功能
await expect(page.locator('.offline-content')).toBeVisible();
// 恢复在线
await networkSimulator.goOnline();
```
#### 3.2.3 性能监控器
**文件位置**: `e2e/src/utils/MobilePerformanceMonitor.ts`
**核心功能**:
```typescript
export class MobilePerformanceMonitor {
constructor(private page: Page) {}
// 运行 Lighthouse 性能测试
async runLighthouseAudit(options: LighthouseOptions): Promise<LighthouseResult>;
// 获取 Core Web Vitals 指标
async getCoreWebVitals(): Promise<CoreWebVitals>;
// 检测性能回归
async detectPerformanceRegression(baseline: PerformanceBaseline): Promise<RegressionReport>;
// 生成性能报告
async generatePerformanceReport(): Promise<PerformanceReport>;
// 监控资源加载
async monitorResourceLoading(): Promise<ResourceLoadingMetrics>;
// 监控内存使用
async monitorMemoryUsage(): Promise<MemoryMetrics>;
}
```
**使用示例**:
```typescript
const performanceMonitor = new MobilePerformanceMonitor(page);
// 运行 Lighthouse 审计
const lighthouseResult = await performanceMonitor.runLighthouseAudit({
onlyAudits: ['first-contentful-paint', 'largest-contentful-paint', 'cumulative-layout-shift'],
});
// 获取 Core Web Vitals
const vitals = await performanceMonitor.getCoreWebVitals();
expect(vitals.LCP).toBeLessThan(2500);
expect(vitals.CLS).toBeLessThan(0.1);
// 检测性能回归
const regressionReport = await performanceMonitor.detectPerformanceRegression(baseline);
expect(regressionReport.hasRegression).toBe(false);
```
### 3.3 测试用例层详细设计
#### 3.3.1 性能测试套件
**文件位置**: `e2e/src/tests/mobile/performance/mobile-performance.spec.ts`
**测试用例结构**:
```typescript
test.describe('移动端性能测试 @mobile @performance', () => {
const devices = getMobileDevices();
const networkConfigs = ['wifi-fast', '4g-lte', '3g-fast', '2g-slow'];
for (const device of devices) {
for (const network of networkConfigs) {
test(`${device.name} - ${network} - 首屏加载性能`, async ({ page, context }) => {
// 设置设备和网络条件
await page.setViewportSize(device.viewport);
await new NetworkSimulator(context).setNetworkCondition(networkConfigs[network]);
// 运行性能测试
const monitor = new MobilePerformanceMonitor(page);
await page.goto('/');
const vitals = await monitor.getCoreWebVitals();
// 验证性能指标
expect(vitals.LCP).toBeLessThan(getPerformanceThreshold(device.name, network, 'LCP'));
expect(vitals.FCP).toBeLessThan(getPerformanceThreshold(device.name, network, 'FCP'));
expect(vitals.CLS).toBeLessThan(getPerformanceThreshold(device.name, network, 'CLS'));
});
}
}
test('移动端 - 交互响应性能', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto('/');
const monitor = new MobilePerformanceMonitor(page);
// 测量点击响应时间
const button = page.locator('button').first();
const startTime = Date.now();
await button.click();
const responseTime = Date.now() - startTime;
expect(responseTime).toBeLessThan(100);
});
test('移动端 - 内存泄漏检测', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
await page.goto('/');
const monitor = new MobilePerformanceMonitor(page);
// 执行多次页面导航
const initialMemory = await monitor.monitorMemoryUsage();
for (let i = 0; i < 10; i++) {
await page.goto('/about');
await page.goto('/');
}
const finalMemory = await monitor.monitorMemoryUsage();
// 验证内存增长在合理范围内
const memoryGrowth = finalMemory.usedJSHeapSize - initialMemory.usedJSHeapSize;
expect(memoryGrowth).toBeLessThan(10 * 1024 * 1024); // 10MB
});
});
```
#### 3.3.2 兼容性测试套件
**文件位置**: `e2e/src/tests/mobile/compatibility/device-compatibility.spec.ts`
**测试用例结构**:
```typescript
test.describe('移动端兼容性测试 @mobile @compatibility', () => {
const devices = getMobileDevices();
for (const device of devices) {
test.describe(`${device.name} 兼容性`, () => {
test('布局适配正确', async ({ page }) => {
await page.setViewportSize(device.viewport);
await page.goto('/');
// 验证关键元素可见
await expect(page.locator('header')).toBeVisible();
await expect(page.locator('main')).toBeVisible();
await expect(page.locator('footer')).toBeVisible();
// 验证布局没有溢出
const bodyWidth = await page.evaluate(() => document.body.scrollWidth);
const viewportWidth = device.viewport.width;
expect(bodyWidth).toBeLessThanOrEqual(viewportWidth);
});
test('导航功能正常', async ({ page }) => {
await page.setViewportSize(device.viewport);
await page.goto('/');
// 测试移动菜单
const menuButton = page.locator('button[aria-label="打开菜单"]');
if (await menuButton.isVisible()) {
await menuButton.click();
await expect(page.locator('#mobile-menu')).toBeVisible();
}
// 测试页面导航
await page.goto('/about');
await expect(page).toHaveURL(/.*about.*/);
});
test('表单功能正常', async ({ page }) => {
await page.setViewportSize(device.viewport);
await page.goto('/contact');
// 测试表单输入
const nameInput = page.locator('input[name="name"]');
await nameInput.fill('测试用户');
const nameValue = await nameInput.inputValue();
expect(nameValue).toBe('测试用户');
});
});
}
test('跨设备布局一致性', async ({ page }) => {
const screenshots: Buffer[] = [];
for (const device of devices) {
await page.setViewportSize(device.viewport);
await page.goto('/');
await page.waitForLoadState('networkidle');
const screenshot = await page.screenshot();
screenshots.push(screenshot);
}
// 对比截图,验证布局一致性
for (let i = 1; i < screenshots.length; i++) {
const similarity = await compareImages(screenshots[0], screenshots[i]);
expect(similarity).toBeGreaterThan(0.85); // 85% 相似度
}
});
});
```
#### 3.3.3 手势交互测试套件
**文件位置**: `e2e/src/tests/mobile/gesture/gesture-interaction.spec.ts`
**测试用例结构**:
```typescript
test.describe('移动端手势交互测试 @mobile @gesture', () => {
let gestureSimulator: GestureSimulator;
test.beforeEach(async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 });
gestureSimulator = new GestureSimulator(page);
});
test('单指滑动 - 页面滚动', async ({ page }) => {
await page.goto('/');
const initialScrollY = await page.evaluate(() => window.scrollY);
expect(initialScrollY).toBe(0);
// 向上滑动
await gestureSimulator.swipe({
startX: 200,
startY: 600,
endX: 200,
endY: 200,
duration: 500,
});
const afterScrollY = await page.evaluate(() => window.scrollY);
expect(afterScrollY).toBeGreaterThan(0);
});
test('双指捏合 - 图片缩放', async ({ page }) => {
await page.goto('/products');
const image = page.locator('.product-image').first();
await image.click();
// 双指放大
await gestureSimulator.pinch({
centerX: 200,
centerY: 300,
startDistance: 100,
endDistance: 50,
duration: 300,
});
// 验证图片已放大
const transform = await image.evaluate((el) => {
const style = window.getComputedStyle(el);
return style.transform;
});
expect(transform).toContain('scale');
});
test('长按 - 上下文菜单', async ({ page }) => {
await page.goto('/');
const card = page.locator('.card').first();
// 长按卡片
await gestureSimulator.longPress(card, 1000);
// 验证上下文菜单出现
await expect(page.locator('.context-menu')).toBeVisible();
});
test('双击 - 图片放大', async ({ page }) => {
await page.goto('/products');
const image = page.locator('.product-image').first();
// 双击图片
await gestureSimulator.doubleTap(image);
// 验证图片已放大
const transform = await image.evaluate((el) => {
const style = window.getComputedStyle(el);
return style.transform;
});
expect(transform).toContain('scale');
});
test('拖拽 - 卡片排序', async ({ page }) => {
await page.goto('/products');
const firstCard = page.locator('.card').first();
const secondCard = page.locator('.card').nth(1);
const firstCardInitialPosition = await firstCard.boundingBox();
// 拖拽第一个卡片到第二个卡片位置
await gestureSimulator.drag({
source: firstCard,
target: secondCard,
duration: 500,
});
// 验证卡片位置已改变
const firstCardFinalPosition = await firstCard.boundingBox();
expect(firstCardFinalPosition.y).toBeGreaterThan(firstCardInitialPosition.y);
});
test('快速滑动 - 下拉刷新', async ({ page }) => {
await page.goto('/news');
// 快速向下滑动
await gestureSimulator.quickSwipeDown();
// 验证刷新指示器出现
await expect(page.locator('.refresh-indicator')).toBeVisible();
// 等待刷新完成
await page.waitForSelector('.refresh-indicator', { state: 'hidden', timeout: 5000 });
});
});
```
#### 3.3.4 PWA 功能测试套件
**文件位置**: `e2e/src/tests/mobile/pwa/pwa-functionality.spec.ts`
**测试用例结构**:
```typescript
test.describe('移动端 PWA 功能测试 @mobile @pwa', () => {
test('Service Worker 注册成功', async ({ page }) => {
await page.goto('/');
// 等待 Service Worker 注册
const swRegistration = await page.evaluate(() => {
return navigator.serviceWorker.getRegistration();
});
expect(swRegistration).toBeTruthy();
});
test('离线缓存功能正常', async ({ page, context }) => {
// 首次访问,缓存资源
await page.goto('/');
await page.waitForLoadState('networkidle');
// 切换到离线模式
await context.setOffline(true);
// 刷新页面,应该从缓存加载
await page.reload();
await expect(page.locator('header')).toBeVisible();
await expect(page.locator('main')).toBeVisible();
// 恢复在线
await context.setOffline(false);
});
test('应用安装提示出现', async ({ page }) => {
await page.goto('/');
// 检查是否有安装提示
const installPrompt = await page.evaluate(() => {
return 'beforeinstallprompt' in window;
});
expect(installPrompt).toBe(true);
});
test('离线页面显示正确', async ({ page, context }) => {
// 首次访问
await page.goto('/');
await page.waitForLoadState('networkidle');
// 切换到离线模式
await context.setOffline(true);
// 访问未缓存的页面
await page.goto('/non-existent-page');
// 验证显示离线页面
await expect(page.locator('.offline-page')).toBeVisible();
});
test('应用更新检测正常', async ({ page }) => {
await page.goto('/');
// 模拟应用更新
await page.evaluate(() => {
window.dispatchEvent(new Event('sw-update'));
});
// 验证更新提示出现
await expect(page.locator('.update-notification')).toBeVisible();
});
});
```
### 3.4 测试执行层详细设计
#### 3.4.1 测试配置扩展
**文件位置**: `e2e/playwright.config.ts`
**扩展配置**:
```typescript
import { defineConfig, devices } from '@playwright/test';
import { getEnvironment } from './src/config/environments';
import { getMobileDevices } from './src/utils/devices';
const env = getEnvironment();
const mobileDevices = getMobileDevices();
export default defineConfig({
testDir: './src/tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: env.retries,
workers: process.env.CI ? 4 : undefined,
reporter: [
['html', { open: 'never' }],
['json', { outputFile: 'test-results/results.json' }],
['junit', { outputFile: 'test-results/junit.xml' }],
['line'],
['list'],
['allure-playwright', {
outputFolder: 'allure-results',
detail: true,
suiteTitle: false,
}],
],
timeout: env.timeout,
expect: {
timeout: 30000
},
use: {
baseURL: env.baseURL,
trace: env.trace,
screenshot: env.screenshot,
video: env.video,
headless: env.headless,
viewport: { width: 1280, height: 720 },
actionTimeout: 30000,
navigationTimeout: 60000,
launchOptions: {
slowMo: env.slowMo,
},
},
projects: [
// 桌面浏览器项目
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
// 移动设备项目
...mobileDevices.map(device => ({
name: `mobile-${device.name.replace(/\s+/g, '-').toLowerCase()}`,
use: {
...devices['Mobile Chrome'],
viewport: device.viewport,
userAgent: device.userAgent,
deviceScaleFactor: device.deviceScaleFactor,
isMobile: true,
},
})),
// 性能测试项目
{
name: 'performance-mobile',
use: {
...devices['Mobile Chrome'],
viewport: { width: 375, height: 667 },
isMobile: true,
},
testMatch: /.*\.perf\.spec\.ts/,
},
// PWA 测试项目
{
name: 'pwa-mobile',
use: {
...devices['Mobile Chrome'],
viewport: { width: 375, height: 667 },
isMobile: true,
serviceWorkers: 'allow',
},
testMatch: /.*\.pwa\.spec\.ts/,
},
],
webServer: env.name === 'development' ? {
command: 'cd .. && npm run dev',
url: 'http://localhost:3000',
timeout: 120000,
reuseExistingServer: !process.env.CI,
} : undefined,
});
```
#### 3.4.2 并行测试策略
**文件位置**: `e2e/src/utils/TestScheduler.ts`
**核心功能**:
```typescript
export class TestScheduler {
private testQueue: TestTask[] = [];
private runningTasks: Map<string, TestTask> = new Map();
// 添加测试任务
addTest(task: TestTask): void;
// 智能调度测试
async scheduleTests(): Promise<void>;
// 监控测试执行
monitorExecution(): ExecutionStatus;
// 优化执行顺序
optimizeExecutionOrder(): TestTask[];
// 获取执行统计
getExecutionStats(): ExecutionStats;
}
```
**调度策略**:
1. **设备级并行**: 不同设备上的测试用例可以同时执行
2. **网络条件级并行**: 同一设备在不同网络条件下的测试可以并行
3. **依赖关系优化**: 根据测试依赖关系优化执行顺序
4. **资源使用优化**: 根据 CPU、内存使用情况动态调整并行度
#### 3.4.3 测试报告系统
**文件位置**: `e2e/src/utils/MobileTestReporter.ts`
**核心功能**:
```typescript
export class MobileTestReporter {
// 生成移动端测试概览
generateOverview(): TestOverview;
// 生成设备兼容性报告
generateCompatibilityReport(): CompatibilityReport;
// 生成性能分析报告
generatePerformanceReport(): PerformanceReport;
// 生成网络环境影响报告
generateNetworkImpactReport(): NetworkImpactReport;
// 生成 HTML 报告
generateHtmlReport(): string;
// 生成 JSON 报告
generateJsonReport(): object;
// 生成 JUnit 报告
generateJUnitReport(): string;
}
```
**报告内容**:
1. **移动端测试概览**
- 总体通过率
- 各设备测试结果
- 执行时间统计
- 失败用例分析
2. **设备兼容性报告**
- 每个设备上的测试结果对比
- 兼容性问题汇总
- 布局差异截图
- 功能降级情况
3. **性能分析报告**
- Core Web Vitals 指标趋势
- 性能回归检测结果
- 资源加载分析
- 性能优化建议
4. **网络环境影响报告**
- 不同网络条件下的性能对比
- 离线功能验证结果
- 网络切换测试结果
- 弱网优化建议
---
## 4. 实施计划
### 4.1 第一阶段:基础设施扩展(优先级:高)
**时间**: 1-2 周
**任务**:
1. **扩展设备配置**
- [ ] 添加 iPhone 13/14/15 系列配置
- [ ] 添加 Samsung Galaxy 系列配置
- [ ] 添加 Google Pixel 系列配置
- [ ] 添加 iPad Air/Pro 系列配置
- [ ] 更新设备选择工具函数
2. **建立移动端测试数据生成器**
- [ ] 创建 `MobileTestDataGenerator`
- [ ] 实现设备特定数据生成
- [ ] 实现网络配置数据生成
- [ ] 实现性能基准数据生成
3. **扩展 Playwright 配置**
- [ ] 添加移动设备测试项目
- [ ] 配置性能测试项目
- [ ] 配置 PWA 测试项目
- [ ] 优化并行执行策略
**验收标准**:
- 所有新设备配置正确
- 测试数据生成器功能完整
- Playwright 配置支持多设备并行测试
### 4.2 第二阶段:核心测试工具开发(优先级:高)
**时间**: 2-3 周
**任务**:
1. **开发手势模拟器**
- [ ] 实现 `GestureSimulator`
- [ ] 实现单指滑动功能
- [ ] 实现双指捏合功能
- [ ] 实现长按功能
- [ ] 实现双击功能
- [ ] 实现拖拽功能
- [ ] 编写使用文档和示例
2. **开发网络环境模拟器**
- [ ] 实现 `NetworkSimulator`
- [ ] 实现网络条件设置
- [ ] 实现离线模式切换
- [ ] 实现网络切换模拟
- [ ] 实现网络请求监控
- [ ] 编写使用文档和示例
3. **集成 Lighthouse 性能测试**
- [ ] 实现 `MobilePerformanceMonitor`
- [ ] 集成 Lighthouse API
- [ ] 实现 Core Web Vitals 监控
- [ ] 实现性能回归检测
- [ ] 实现性能报告生成
- [ ] 编写使用文档和示例
4. **建立移动端测试报告系统**
- [ ] 实现 `MobileTestReporter`
- [ ] 实现测试概览报告
- [ ] 实现兼容性报告
- [ ] 实现性能分析报告
- [ ] 实现网络影响报告
- [ ] 支持多种输出格式
**验收标准**:
- 手势模拟器支持所有基本手势
- 网络模拟器支持所有网络场景
- 性能监控器能准确测量 Core Web Vitals
- 测试报告系统生成完整的移动端报告
### 4.3 第三阶段:测试用例开发(优先级:中)
**时间**: 3-4 周
**任务**:
1. **开发性能测试套件**
- [ ] 首屏加载性能测试
- [ ] 页面交互响应测试
- [ ] 资源加载优化测试
- [ ] 内存泄漏检测测试
- [ ] 建立性能基准和阈值
2. **开发兼容性测试套件**
- [ ] 不同屏幕尺寸布局测试
- [ ] 不同操作系统功能测试
- [ ] 不同浏览器渲染测试
- [ ] 不同系统版本降级测试
- [ ] 建立设备测试矩阵
3. **开发手势交互测试套件**
- [ ] 单指滑动测试
- [ ] 双指捏合缩放测试
- [ ] 长按操作测试
- [ ] 双击操作测试
- [ ] 拖拽操作测试
4. **开发 PWA 功能测试套件**
- [ ] Service Worker 注册测试
- [ ] 离线缓存功能测试
- [ ] 应用安装测试
- [ ] 推送通知测试
- [ ] 应用更新测试
**验收标准**:
- 所有测试用例通过
- 测试覆盖率达到预期目标
- 测试执行时间在可接受范围内
### 4.4 第四阶段:高级功能和优化(优先级:低)
**时间**: 2-3 周
**任务**:
1. **开发复杂手势模拟器**
- [ ] 实现三指手势
- [ ] 实现复杂手势组合
- [ ] 优化手势识别算法
2. **完善网络环境模拟**
- [ ] 实现网络丢包模拟
- [ ] 实现网络抖动模拟
- [ ] 实现自定义网络配置
3. **建立 PWA 功能测试套件**
- [ ] 完善离线功能测试
- [ ] 完善推送通知测试
- [ ] 完善应用更新测试
4. **优化测试执行效率**
- [ ] 实现智能测试调度
- [ ] 优化并行执行策略
- [ ] 实现测试缓存机制
5. **完善测试报告系统**
- [ ] 优化报告可视化
- [ ] 添加趋势分析
- [ ] 添加性能对比功能
**验收标准**:
- 所有高级功能正常工作
- 测试执行效率提升 30% 以上
- 测试报告更加直观和有用
---
## 5. 技术栈
### 5.1 核心技术
- **测试框架**: Playwright
- **性能测试**: Lighthouse
- **网络模拟**: Chrome DevTools Protocol
- **报告生成**: Allure、HTML、JSON、JUnit
- **类型系统**: TypeScript
### 5.2 依赖包
```json
{
"dependencies": {
"@playwright/test": "^1.40.0",
"lighthouse": "^11.0.0",
"chrome-remote-interface": "^0.33.0",
"@types/lighthouse": "^11.0.0"
}
}
```
### 5.3 开发工具
- **代码编辑器**: VS Code
- **版本控制**: Git
- **包管理器**: npm
- **构建工具**: TypeScript Compiler
---
## 6. 成功标准
### 6.1 功能完整性
- ✅ 支持所有主流移动设备(iPhone、Android、iPad
- ✅ 支持所有基本手势操作(滑动、点击、长按、双击、拖拽)
- ✅ 支持所有网络条件(在线、离线、弱网、网络切换)
- ✅ 支持所有 Core Web Vitals 指标监控
- ✅ 支持完整的 PWA 功能测试
### 6.2 测试覆盖率
- ✅ 移动端功能测试覆盖率 ≥ 80%
- ✅ 移动端性能测试覆盖率 ≥ 70%
- ✅ 移动端兼容性测试覆盖率 ≥ 90%
- ✅ 移动端 PWA 功能测试覆盖率 ≥ 85%
### 6.3 性能指标
- ✅ 测试执行时间 ≤ 30 分钟(完整测试套件)
- ✅ 测试通过率 ≥ 95%
- ✅ 性能回归检测准确率 ≥ 90%
- ✅ 测试报告生成时间 ≤ 1 分钟
### 6.4 可维护性
- ✅ 代码覆盖率 ≥ 80%
- ✅ 文档完整性 ≥ 90%
- ✅ 代码审查通过率 100%
- ✅ 技术债务控制良好
---
## 7. 风险和挑战
### 7.1 技术风险
1. **Playwright 限制**
- 风险: 某些高级手势可能无法完美模拟
- 缓解: 优先实现基本手势,高级手势作为可选功能
2. **网络模拟准确性**
- 风险: 模拟的网络条件可能与真实网络有差异
- 缓解: 使用真实设备进行验证和校准
3. **性能测试稳定性**
- 风险: 性能测试结果可能受环境影响
- 缓解: 建立稳定的测试环境,多次测试取平均值
### 7.2 项目风险
1. **时间压力**
- 风险: 项目时间可能紧张
- 缓解: 采用敏捷开发,优先实现高优先级功能
2. **资源限制**
- 风险: 开发资源可能不足
- 缓解: 合理分配资源,必要时调整优先级
3. **需求变更**
- 风险: 需求可能在开发过程中变更
- 缓解: 采用迭代开发,及时响应需求变更
---
## 8. 后续优化方向
### 8.1 短期优化(1-3 个月)
1. **真实设备集成**
- 集成 BrowserStack 或 Sauce Labs
- 支持真实设备测试
- 建立设备云管理
2. **测试数据管理**
- 建立测试数据仓库
- 支持数据版本控制
- 实现数据复用机制
3. **测试结果分析**
- 建立测试结果趋势分析
- 实现智能问题诊断
- 提供优化建议
### 8.2 中期优化(3-6 个月)
1. **AI 辅助测试**
- 使用 AI 生成测试用例
- 智能测试用例选择
- 自动化问题定位
2. **测试平台化**
- 构建测试管理平台
- 支持测试计划管理
- 实现测试资源调度
3. **持续监控**
- 建立生产环境监控
- 实现实时性能监控
- 集成告警机制
### 8.3 长期优化(6-12 个月)
1. **测试即代码**
- 将测试代码与生产代码同等对待
- 建立测试代码审查机制
- 实现测试代码质量监控
2. **测试左移**
- 在需求分析阶段引入测试
- 实现测试驱动开发
- 建立质量门禁
3. **测试右移**
- 在生产环境进行测试
- 实现金丝雀发布
- 建立快速回滚机制
---
## 9. 总结
本设计文档详细描述了移动端测试完善方案的整体架构、详细设计、实施计划和技术栈。通过四个阶段的实施,我们将建立一个全面完善的移动端测试体系,覆盖性能、兼容性、手势交互、PWA 功能等方面。
该方案具有以下优势:
1. **渐进式实施**: 在现有框架基础上逐步扩展,风险可控
2. **全面覆盖**: 覆盖移动端测试的所有重要方面
3. **可扩展性**: 架构设计支持后续功能扩展
4. **可维护性**: 代码结构清晰,易于维护和优化
通过实施本方案,我们将显著提升移动端测试的质量和效率,为用户提供更好的移动端体验。
---
**文档版本**: 1.0
**最后更新**: 2026-03-05
**文档状态**: 待审核