Playwright 第14章:移动端测试

随着移动互联网的普及,移动端 Web 应用和 Hybrid 应用的测试需求越来越大。Playwright 提供了强大的移动端测试能力,让你可以在桌面环境中模拟各种移动设备的行为,无需连接真实设备即可完成大部分测试工作。

移动端测试概述

移动端测试的核心挑战在于设备的多样性和环境的复杂性。不同屏幕尺寸、操作系统版本、浏览器内核、网络条件等因素都可能导致应用表现不一致。Playwright 通过以下方式解决这些问题:

  • 设备模拟(Device Mode):预置主流移动设备的配置参数
  • 触摸事件模拟:模拟手指点击、滑动、捏合等手势
  • 视口(Viewport)控制:精确控制浏览器视口尺寸
  • 地理定位模拟:模拟不同的 GPS 位置
  • 网络条件模拟:模拟 3G、4G 等网络环境

Playwright 的移动端测试不需要真实的移动设备或模拟器,所有测试都在常规浏览器中运行,但行为和效果与真实设备保持一致。

设备模拟

使用内置设备描述

Playwright 内置了大量主流移动设备的配置描述,包括 iPhone、iPad、Samsung Galaxy 等系列。通过 devices 对象可以直接获取这些配置:

import { test, expect, devices } from '@playwright/test';

// 获取 iPhone 13 的设备配置
const iPhone13 = devices['iPhone 13'];

test('在 iPhone 13 上测试页面', async () => {
  const browser = await chromium.launch();
  const context = await browser.newContext({
    ...iPhone13,
  });
  const page = await context.newPage();
  await page.goto('https://example.com');
  // 验证移动端布局
  await expect(page.locator('.mobile-header')).toBeVisible();
  await browser.close();
});

常用设备列表

Playwright 支持数十种设备的模拟,以下是常用设备:

设备名称屏幕尺寸设备像素比类型
iPhone 13390x8443手机
iPhone 13 Pro Max428x9263手机
iPhone SE375x6672手机
iPad Pro 11834x11942平板
iPad Mini768x10242平板
Samsung Galaxy S9+412x8464手机
Samsung Galaxy Tab S4712x11382.25平板
Pixel 5393x8513手机

自定义设备配置

除了使用内置设备,你也可以自定义设备配置:

const customDevice = {
  viewport: { width: 375, height: 812 },
  userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)',
  deviceScaleFactor: 3,
  isMobile: true,
  hasTouch: true,
};

test('自定义移动设备测试', async () => {
  const context = await browser.newContext(customDevice);
  const page = await context.newPage();
  await page.goto('https://example.com');
});

Viewport 设置

Viewport(视口)是移动端测试中最基本的设置项,它决定了浏览器渲染区域的尺寸。

import { test } from '@playwright/test';

test('不同视口尺寸测试', async ({ browser }) => {
  // 小屏手机
  const smallContext = await browser.newContext({
    viewport: { width: 320, height: 568 }, // iPhone SE
  });
  // 大屏手机
  const largeContext = await browser.newContext({
    viewport: { width: 414, height: 896 }, // iPhone 11 Pro Max
  });
  // 平板
  const tabletContext = await browser.newContext({
    viewport: { width: 1024, height: 1366 }, // iPad Pro
  });

  const page = await smallContext.newPage();
  await page.goto('https://example.com');

  // 截取不同视口下的页面快照进行对比
  await page.screenshot({ path: 'mobile-small.png' });
});

响应式测试策略

对于响应式网站,建议在多个断点处进行测试:

const viewports = [
  { width: 375, height: 667 },  // 手机
  { width: 768, height: 1024 }, // 平板竖屏
  { width: 1024, height: 768 }, // 平板横屏
  { width: 1440, height: 900 }, // 桌面
];

for (const viewport of viewports) {
  test(`视口 ${viewport.width}x${viewport.height} 下的页面布局`, async ({ browser }) => {
    const context = await browser.newContext({ viewport });
    const page = await context.newPage();
    await page.goto('https://example.com');
    await expect(page.locator('.main-content')).toBeVisible();
    await page.screenshot({ path: `screenshot-${viewport.width}.png` });
  });
}

触摸事件模拟

移动设备使用触摸交互,Playwright 提供了完整的触摸事件 API。

启用触摸事件

设置 hasTouch: true 即可启用触摸事件模拟:

const context = await browser.newContext({
  hasTouch: true,
  isMobile: true,
});
const page = await context.newPage();

触摸操作

// 模拟点击
await page.tap('.button');

// 模拟触摸滑动(滚动)
await page.touchscreen.swipe(0, 200); // 水平滑动

// 触摸长按(通过 hover 模拟)
await page.locator('.long-press-item').hover({ force: true });
await page.waitForTimeout(1000);

手势操作

Playwright 不直接提供捏合、缩放等手势 API,但可以通过模拟多点触控来实现:

// 模拟双指捏合缩放
async function pinchZoom(page, scale: number) {
  const center = await page.locator('.pinch-target').boundingBox();
  if (!center) return;

  const startX = center.x + center.width / 2;
  const startY = center.y + center.height / 2;

  // 使用 CDP 协议模拟多点触控
  await page.touchscreen.tap(startX, startY);
  // 部分复杂手势需要结合 Chrome DevTools Protocol
  const session = await page.context().newCDPSession(page);
  await session.send('Input.dispatchTouchEvent', {
    type: 'touchStart',
    touchPoints: [
      { x: startX, y: startY, id: 0 },
      { x: startX + 50, y: startY, id: 1 },
    ],
  });
  // ... 后续 touchMove、touchEnd 事件
}

// 模拟滑动翻页
async function swipePage(page, direction: 'left' | 'right') {
  const viewport = page.viewportSize()!;
  const startX = direction === 'left' ? viewport.width * 0.8 : viewport.width * 0.2;
  const endX = direction === 'left' ? viewport.width * 0.2 : viewport.width * 0.8;

  await page.mouse.move(startX, viewport.height / 2);
  await page.mouse.down();
  await page.mouse.move(endX, viewport.height / 2, { steps: 10 });
  await page.mouse.up();
}

地理定位模拟

许多移动应用依赖地理位置信息。Playwright 可以在浏览器上下文中预设地理位置:

import { test } from '@playwright/test';

test('地理定位测试', async ({ browser }) => {
  const context = await browser.newContext({
    geolocation: {
      latitude: 31.2304,   // 上海
      longitude: 121.4737,
    },
    permissions: ['geolocation'],
  });

  const page = await context.newPage();
  await page.goto('https://example.com/map');
  await page.click('.get-location');

  // 验证页面显示的位置信息
  await expect(page.locator('.location-info')).toContainText('上海');
});

test('动态修改地理位置', async ({ page, context }) => {
  await page.goto('https://example.com/map');

  // 授予地理定位权限
  await context.grantPermissions(['geolocation']);

  // 动态修改地理位置
  await context.setGeolocation({
    latitude: 39.9042,   // 北京
    longitude: 116.4074,
  });

  await page.reload();
  await page.click('.get-location');
  await expect(page.locator('.location-info')).toContainText('北京');
});

实际设备测试 vs 模拟器

Playwright 的设备模拟虽然强大,但与真实设备测试仍有区别:

方面Playwright 设备模拟实际设备测试
执行速度快,无需部署到设备慢,需要部署和配置
硬件特性无法模拟(CPU、内存、传感器)真实硬件表现
操作系统行为模拟浏览器行为,非 OS 层真实 OS 行为
网络条件可通过 API 模拟真实网络环境
调试能力强,可用 DevTools受限
覆盖范围一次性覆盖多设备每台设备单独测试
成本需要维护设备集群

推荐策略

  1. CI 流水线中使用 Playwright 模拟:覆盖主要设备和断点
  2. 关键流程在真实设备上验证:支付、登录等核心链路
  3. 使用云测试平台补充:BrowserStack、Sauce Labs 等平台提供真实设备
  4. 性能测试使用真实设备:设备模拟无法反映真实的性能表现

Chrome DevTools 移动端调试

Playwright 可以集成 Chrome DevTools Protocol (CDP) 进行移动端调试:

test('使用 CDP 调试移动端页面', async ({ browser }) => {
  const context = await browser.newContext({
    ...devices['iPhone 13'],
  });
  const page = await context.newPage();

  // 开启 DevTools 调试
  const cdpSession = await context.newCDPSession(page);
  await cdpSession.send('Page.enable');
  await cdpSession.send('Page.setTouchEmulationEnabled', {
    enabled: true,
  });

  await page.goto('https://example.com');

  // 监听控制台日志
  cdpSession.on('Log.entryAdded', (entry) => {
    console.log(`[${entry.level}] ${entry.text}`);
  });

  // 捕获性能指标
  const metrics = await cdpSession.send('Performance.getMetrics');
  console.log('性能指标:', metrics);
});

慢速网络模拟

模拟移动设备常见的弱网环境:

test('弱网环境测试', async ({ browser }) => {
  const context = await browser.newContext({
    ...devices['iPhone 13'],
  });
  const page = await context.newPage();

  const cdpSession = await context.newCDPSession(page);
  // 模拟 3G 网络
  await cdpSession.send('Network.emulateNetworkConditions', {
    offline: false,
    latency: 100,          // 延迟 100ms
    downloadThroughput: 750 * 1024,   // 下载 750kbps
    uploadThroughput: 250 * 1024,     // 上传 250kbps
  });

  // 监听请求时间
  await page.goto('https://example.com', { waitUntil: 'networkidle' });
  const timing = await page.evaluate(() => performance.timing);
  console.log('页面加载耗时:', timing.loadEventEnd - timing.navigationStart);
});

总结

Playwright 的移动端测试能力覆盖了设备模拟、触摸事件、地理定位、网络条件等核心场景。通过内置设备库和灵活的 API,你可以在 CI 流水线中自动执行移动端测试,快速发现布局适配、交互兼容性等问题。结合 Chrome DevTools Protocol,还可以进行更深层次的性能分析和调试。虽然不能完全替代真实设备测试,但 Playwright 的设备模拟已经足以覆盖绝大多数移动端 Web 测试需求,是实现高效移动端质量保障的重要工具。