39 KiB
移动端测试完善方案设计文档
创建日期: 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 测试数据层
职责: 提供移动端测试所需的所有数据支持
核心组件:
-
移动端测试数据生成器
- 扩展现有的
TestDataGenerator - 增加设备特定的测试数据(User Agent、触摸事件数据)
- 支持不同网络条件下的测试数据
- 扩展现有的
-
设备配置数据
- 扩展
devices.ts,增加更多真实设备配置 - 支持 iPhone 13/14/15 系列
- 支持 Samsung Galaxy、Google Pixel 系列
- 支持 iPad Air/Pro 系列
- 扩展
-
网络配置数据
- 预设网络配置(2G、3G、4G、WiFi)
- 自定义网络配置(延迟、丢包率、带宽)
- 网络切换场景配置
-
性能基准数据
- Core Web Vitals 基准值
- 不同设备类型的性能阈值
- 性能回归检测基准
-
测试数据仓库
- 集中管理所有测试数据
- 支持数据版本控制
- 提供数据查询和复用机制
2.2 测试工具层
职责: 提供移动端测试所需的专用工具和功能
核心组件:
-
手势模拟器 (GestureSimulator)
- 基于 Playwright Touch API 构建
- 支持的手势类型:
- 单指滑动(水平、垂直,支持速度和距离控制)
- 双指捏合缩放(支持缩放比例控制)
- 长按操作(支持自定义时长)
- 双击操作(支持自定义间隔时间)
- 拖拽操作(支持方向和距离控制)
- 提供统一的 API 接口
- 支持链式调用和参数化配置
-
网络环境模拟器 (NetworkSimulator)
- 利用 Chrome DevTools Protocol 实现
- 支持的网络场景:
- 弱网环境(2G、3G、4G 不同信号强度)
- 离线模式(完全断网)
- 网络切换(在线→离线、弱网→强网)
- 网络延迟和丢包模拟
- 提供预设配置和自定义配置
- 支持动态切换网络环境
-
性能监控器 (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)
- 建立移动端性能基准
- 支持性能回归检测
- 生成详细性能分析报告
-
设备兼容性验证工具 (CompatibilityValidator)
- 跨设备布局验证
- 功能兼容性检查
- 渲染一致性对比
- 系统版本降级测试
2.3 测试用例层
职责: 组织和管理所有移动端测试用例
核心组件:
-
性能测试套件 (PerformanceTestSuite)
- 首屏加载性能测试
- 页面交互响应测试
- 资源加载优化测试
- 内存泄漏检测测试
- 每个测试用例设置明确的性能阈值
-
兼容性测试套件 (CompatibilityTestSuite)
- 不同屏幕尺寸的布局适配测试
- 不同操作系统的功能兼容性测试
- 不同浏览器的渲染一致性测试
- 不同系统版本的功能降级测试
- 采用参数化测试方式
-
手势交互测试套件 (GestureTestSuite)
- 单指滑动测试(页面滚动、轮播图切换)
- 双指捏合缩放测试(图片查看、地图缩放)
- 长按操作测试(上下文菜单、多选操作)
- 双击操作测试(图片放大、点赞功能)
- 拖拽操作测试(卡片排序、元素移动)
-
PWA 功能测试套件 (PWATestSuite)
- Service Worker 注册测试
- 离线缓存功能测试
- 应用安装测试
- 推送通知测试
- 应用更新测试
2.4 测试执行层
职责: 管理测试的执行、调度和报告
核心组件:
-
测试配置扩展
- 扩展现有 Playwright 配置
- 增加移动设备项目配置
- 增加网络环境项目配置
- 增加性能测试专用项目配置
- 支持动态设备选择和网络条件切换
-
并行测试策略
- 设备级并行(不同设备上的测试用例同时执行)
- 网络条件级并行(同一设备在不同网络条件下并行测试)
- 智能测试调度(根据依赖关系和资源使用优化执行顺序)
- 显著缩短测试执行时间
-
测试报告系统
- 移动端测试概览(通过率、失败率、执行时间)
- 设备兼容性报告(每个设备上的测试结果对比)
- 性能分析报告(Core Web Vitals 趋势、性能回归检测)
- 网络环境影响报告(不同网络条件下的性能对比)
- 支持多种输出格式(HTML、JSON、JUnit)
3. 详细设计
3.1 测试数据层详细设计
3.1.1 移动端测试数据生成器
文件位置: e2e/src/utils/MobileTestDataGenerator.ts
核心功能:
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
新增设备配置:
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
网络配置示例:
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
核心功能:
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>;
}
使用示例:
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
核心功能:
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[]>;
}
使用示例:
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
核心功能:
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>;
}
使用示例:
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
测试用例结构:
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
测试用例结构:
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
测试用例结构:
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
测试用例结构:
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
扩展配置:
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
核心功能:
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;
}
调度策略:
- 设备级并行: 不同设备上的测试用例可以同时执行
- 网络条件级并行: 同一设备在不同网络条件下的测试可以并行
- 依赖关系优化: 根据测试依赖关系优化执行顺序
- 资源使用优化: 根据 CPU、内存使用情况动态调整并行度
3.4.3 测试报告系统
文件位置: e2e/src/utils/MobileTestReporter.ts
核心功能:
export class MobileTestReporter {
// 生成移动端测试概览
generateOverview(): TestOverview;
// 生成设备兼容性报告
generateCompatibilityReport(): CompatibilityReport;
// 生成性能分析报告
generatePerformanceReport(): PerformanceReport;
// 生成网络环境影响报告
generateNetworkImpactReport(): NetworkImpactReport;
// 生成 HTML 报告
generateHtmlReport(): string;
// 生成 JSON 报告
generateJsonReport(): object;
// 生成 JUnit 报告
generateJUnitReport(): string;
}
报告内容:
-
移动端测试概览
- 总体通过率
- 各设备测试结果
- 执行时间统计
- 失败用例分析
-
设备兼容性报告
- 每个设备上的测试结果对比
- 兼容性问题汇总
- 布局差异截图
- 功能降级情况
-
性能分析报告
- Core Web Vitals 指标趋势
- 性能回归检测结果
- 资源加载分析
- 性能优化建议
-
网络环境影响报告
- 不同网络条件下的性能对比
- 离线功能验证结果
- 网络切换测试结果
- 弱网优化建议
4. 实施计划
4.1 第一阶段:基础设施扩展(优先级:高)
时间: 1-2 周
任务:
-
扩展设备配置
- 添加 iPhone 13/14/15 系列配置
- 添加 Samsung Galaxy 系列配置
- 添加 Google Pixel 系列配置
- 添加 iPad Air/Pro 系列配置
- 更新设备选择工具函数
-
建立移动端测试数据生成器
- 创建
MobileTestDataGenerator类 - 实现设备特定数据生成
- 实现网络配置数据生成
- 实现性能基准数据生成
- 创建
-
扩展 Playwright 配置
- 添加移动设备测试项目
- 配置性能测试项目
- 配置 PWA 测试项目
- 优化并行执行策略
验收标准:
- 所有新设备配置正确
- 测试数据生成器功能完整
- Playwright 配置支持多设备并行测试
4.2 第二阶段:核心测试工具开发(优先级:高)
时间: 2-3 周
任务:
-
开发手势模拟器
- 实现
GestureSimulator类 - 实现单指滑动功能
- 实现双指捏合功能
- 实现长按功能
- 实现双击功能
- 实现拖拽功能
- 编写使用文档和示例
- 实现
-
开发网络环境模拟器
- 实现
NetworkSimulator类 - 实现网络条件设置
- 实现离线模式切换
- 实现网络切换模拟
- 实现网络请求监控
- 编写使用文档和示例
- 实现
-
集成 Lighthouse 性能测试
- 实现
MobilePerformanceMonitor类 - 集成 Lighthouse API
- 实现 Core Web Vitals 监控
- 实现性能回归检测
- 实现性能报告生成
- 编写使用文档和示例
- 实现
-
建立移动端测试报告系统
- 实现
MobileTestReporter类 - 实现测试概览报告
- 实现兼容性报告
- 实现性能分析报告
- 实现网络影响报告
- 支持多种输出格式
- 实现
验收标准:
- 手势模拟器支持所有基本手势
- 网络模拟器支持所有网络场景
- 性能监控器能准确测量 Core Web Vitals
- 测试报告系统生成完整的移动端报告
4.3 第三阶段:测试用例开发(优先级:中)
时间: 3-4 周
任务:
-
开发性能测试套件
- 首屏加载性能测试
- 页面交互响应测试
- 资源加载优化测试
- 内存泄漏检测测试
- 建立性能基准和阈值
-
开发兼容性测试套件
- 不同屏幕尺寸布局测试
- 不同操作系统功能测试
- 不同浏览器渲染测试
- 不同系统版本降级测试
- 建立设备测试矩阵
-
开发手势交互测试套件
- 单指滑动测试
- 双指捏合缩放测试
- 长按操作测试
- 双击操作测试
- 拖拽操作测试
-
开发 PWA 功能测试套件
- Service Worker 注册测试
- 离线缓存功能测试
- 应用安装测试
- 推送通知测试
- 应用更新测试
验收标准:
- 所有测试用例通过
- 测试覆盖率达到预期目标
- 测试执行时间在可接受范围内
4.4 第四阶段:高级功能和优化(优先级:低)
时间: 2-3 周
任务:
-
开发复杂手势模拟器
- 实现三指手势
- 实现复杂手势组合
- 优化手势识别算法
-
完善网络环境模拟
- 实现网络丢包模拟
- 实现网络抖动模拟
- 实现自定义网络配置
-
建立 PWA 功能测试套件
- 完善离线功能测试
- 完善推送通知测试
- 完善应用更新测试
-
优化测试执行效率
- 实现智能测试调度
- 优化并行执行策略
- 实现测试缓存机制
-
完善测试报告系统
- 优化报告可视化
- 添加趋势分析
- 添加性能对比功能
验收标准:
- 所有高级功能正常工作
- 测试执行效率提升 30% 以上
- 测试报告更加直观和有用
5. 技术栈
5.1 核心技术
- 测试框架: Playwright
- 性能测试: Lighthouse
- 网络模拟: Chrome DevTools Protocol
- 报告生成: Allure、HTML、JSON、JUnit
- 类型系统: TypeScript
5.2 依赖包
{
"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 技术风险
-
Playwright 限制
- 风险: 某些高级手势可能无法完美模拟
- 缓解: 优先实现基本手势,高级手势作为可选功能
-
网络模拟准确性
- 风险: 模拟的网络条件可能与真实网络有差异
- 缓解: 使用真实设备进行验证和校准
-
性能测试稳定性
- 风险: 性能测试结果可能受环境影响
- 缓解: 建立稳定的测试环境,多次测试取平均值
7.2 项目风险
-
时间压力
- 风险: 项目时间可能紧张
- 缓解: 采用敏捷开发,优先实现高优先级功能
-
资源限制
- 风险: 开发资源可能不足
- 缓解: 合理分配资源,必要时调整优先级
-
需求变更
- 风险: 需求可能在开发过程中变更
- 缓解: 采用迭代开发,及时响应需求变更
8. 后续优化方向
8.1 短期优化(1-3 个月)
-
真实设备集成
- 集成 BrowserStack 或 Sauce Labs
- 支持真实设备测试
- 建立设备云管理
-
测试数据管理
- 建立测试数据仓库
- 支持数据版本控制
- 实现数据复用机制
-
测试结果分析
- 建立测试结果趋势分析
- 实现智能问题诊断
- 提供优化建议
8.2 中期优化(3-6 个月)
-
AI 辅助测试
- 使用 AI 生成测试用例
- 智能测试用例选择
- 自动化问题定位
-
测试平台化
- 构建测试管理平台
- 支持测试计划管理
- 实现测试资源调度
-
持续监控
- 建立生产环境监控
- 实现实时性能监控
- 集成告警机制
8.3 长期优化(6-12 个月)
-
测试即代码
- 将测试代码与生产代码同等对待
- 建立测试代码审查机制
- 实现测试代码质量监控
-
测试左移
- 在需求分析阶段引入测试
- 实现测试驱动开发
- 建立质量门禁
-
测试右移
- 在生产环境进行测试
- 实现金丝雀发布
- 建立快速回滚机制
9. 总结
本设计文档详细描述了移动端测试完善方案的整体架构、详细设计、实施计划和技术栈。通过四个阶段的实施,我们将建立一个全面完善的移动端测试体系,覆盖性能、兼容性、手势交互、PWA 功能等方面。
该方案具有以下优势:
- 渐进式实施: 在现有框架基础上逐步扩展,风险可控
- 全面覆盖: 覆盖移动端测试的所有重要方面
- 可扩展性: 架构设计支持后续功能扩展
- 可维护性: 代码结构清晰,易于维护和优化
通过实施本方案,我们将显著提升移动端测试的质量和效率,为用户提供更好的移动端体验。
文档版本: 1.0
最后更新: 2026-03-05
文档状态: 待审核