1. Viewport emulation vs real devices
Playwright's device emulation covers 80 % of mobile testing at 10 % of the cost. The other 20 % needs real hardware.
When to reach for it: Every PR: emulation. Pre-release: real-device smoke test. Emulation is fast and deterministic; real devices are slow and noisy but catch what emulation can't.
import { test, devices } from '@playwright/test';
// Use built-in device descriptors โ iPhone 15, Pixel 7, Galaxy S23, etc.
test.use({ ...devices['iPhone 15'] });
test('product card is tappable on iPhone 15', async ({ page }) => {
await page.goto('/products/42');
// Card must be at least 44ร44 px for comfortable tap (WCAG 2.5.5 AAA)
const card = page.getByTestId('product-card');
const box = await card.boundingBox();
expect(box?.width).toBeGreaterThanOrEqual(44);
expect(box?.height).toBeGreaterThanOrEqual(44);
});
// Matrix across multiple devices (project config)
// playwright.config.ts
export default defineConfig({
projects: [
{ name: 'iPhone 15', use: { ...devices['iPhone 15'] } },
{ name: 'Pixel 7', use: { ...devices['Pixel 7'] } },
{ name: 'iPad Mini', use: { ...devices['iPad Mini'] } },
],
});Emulation gap
Emulation fakes dimensions and user-agent but runs on desktop Chromium. It MISSES: real Safari rendering bugs, GPU throttling, Android WebView quirks, memory pressure on low-end devices, network-stack differences, and iOS PWA-specific bugs. BrowserStack / SauceLabs Live + real device smoke tests close the gap.
Rule of thumb
Emulation on every PR, real devices on every release. If you must pick one: emulate for coverage, spot-check real devices for the 3 devices representing 80 % of your traffic.