Playwright Mobile Testing: How to Test on Real Devices vs Emulators (2026 Guide)

Struggling with mobile test coverage? This guide walks you through mobile testing, device emulation, cloud platform integration, Android setup, touch events, and CI pipelines with working code examples against a real e-commerce site.

Playwright doesn't run tests on real mobile browsers.

It emulates devices by setting viewport size, user agent, and touch capabilities inside Chromium or WebKit desktop engines.

  • For responsive web testing, that covers most of what you need.

  • For native gestures, hardware sensors, or actual Safari on iOS, it doesn't.

This guide covers what Playwright mobile testing can and can't do, how to configure device emulation, how to connect real Android devices via CDP, and when you need a cloud platform instead.

Playwright mobile testing refers to using the Playwright framework to validate web applications on mobile viewports, either through built-in device emulation (spoofing viewport, user agent, touch support) or by connecting to real physical devices via cloud device providers like LambdaTest, BrowserStack, or PCloudy.

What is Playwright mobile testing and how does it work?

Playwright is a browser automation framework built by Microsoft. It controls Chromium, Firefox, and WebKit (Safari's engine) through a single API. For mobile testing, it operates in two distinct modes.

Mode 1: Device emulation.

Playwright ships with a built-in registry of device descriptors covering over 100 devices. Each descriptor defines viewport width, height, device scale factor, user agent string, and touch support. When you apply a profile like iPhone 13 or Pixel 5, Playwright configures the browser context to mimic that device.

Mode 2: Real device testing.

Playwright can connect to real physical devices through cloud providers. These platforms expose real Android and iOS hardware via WebSocket or CDP (Chrome DevTools Protocol) connections. Playwright targets these remote browsers the same way it targets a local one.

Note: Playwright is designed for mobile web testing. It automates browsers and WebViews, not native mobile app UIs. For native app testing, tools like Appium or Maestro are better suited.

Here is what happens under the hood when you pick a device profile:

  1. Playwright reads the device descriptor from its internal JSON registry.
  2. A new browser context is created with matching viewport, userAgent, deviceScaleFactor, isMobile, and hasTouch properties.
  3. The browser renders the page as if it were running on that device.
  4. All pointer events are dispatched as touch events instead of mouse events.

This approach is fast, free, and completely local.

playwright.config.ts
import { defineConfigdevices } from "@playwright/test";
export default defineConfig({
  projects: [
    {
      name: "Mobile Chrome",
      use: { ...devices["Pixel 5"] },
    },
    {
      name: "Mobile Safari",
      use: { ...devices["iPhone 13"] },
    },
  ],
});

The config above creates two test projects. Every test in your suite runs once on a Pixel 5 emulation and once on an iPhone 13 emulation. Playwright handles viewport resizing, user agent spoofing, and touch event routing automatically.

Track mobile test results easily
See pass/fail trends across devices in one dashboard.
Try free CTA Graphic

Setting up Playwright device emulation (step-by-step)

Let's walk through the full setup from scratch. Every test below targets the TestDino Demo Store at storedemo.testdino.com, so you can run them yourself.

Step 1: Install Playwright

terminal
npm init -y
npm install -D @playwright/test
npx playwright install

The last command downloads browser binaries for Chromium, Firefox, and WebKit.

Step 2: Configure mobile device profiles

Open your playwright.config.ts and add mobile projects using the built-in devices object.

playwright.config.ts
import { defineConfigdevices } from "@playwright/test";
export default defineConfig({
  testDir: "./tests",
  timeout: 30000,
  use: {
    baseURL: "https://storedemo.testdino.com",
    trace: "on-first-retry",
  },
  projects: [
    {
      name: "Desktop Chrome",
      use: { ...devices["Desktop Chrome"] },
    },
    {
      name: "Pixel 5",
      use: { ...devices["Pixel 5"] },
    },
    {
      name: "iPhone 13",
      use: { ...devices["iPhone 13"] },
    },
    {
      name: "Galaxy S9+",
      use: { ...devices["Galaxy S9+"] },
    },
  ],
});

Step 3: Write a test that validates mobile navigation

On desktop, the TestDino Demo Store shows a full navigation bar. On mobile viewports, it collapses into a hamburger menu. This test verifies that behavior.

tests/mobile-navigation.spec.ts
import { testexpectdevices } from '@playwright/test';
test.use({
  ...devices['Pixel 5'],
});

test('test'async ({ page }) => {
  await page.goto('/');
  await page.getByTestId('header-menu-icon').click();
  await page.getByTestId('header-menu-all-products').nth(1).click();
});

Step 4: Write a test that adds a product to cart on mobile

tests/mobile-add-to-cart.spec.ts
import { testexpect } from "@playwright/test";
test("add product to cart on mobile viewport"async ({ page }) => {
  await page.goto("/");

  // Tap "Shop Now" on the hero section
  await page.getByRole("link", { name: "Shop Now" }).tap();

  // Click on the first product card to view details
  await page.locator(".product-card").first().click();

  // Wait for product detail page to load
  await expect(page.getByRole("button", { name: "ADD TO CART" })).toBeVisible();

  // Tap the Add to Cart button
  await page.getByRole("button", { name: "ADD TO CART" }).tap();

  // Verify the cart counter updates (badge shows item count)
  const cartBadge = page.locator('[class*="badge"]').first();
  await expect(cartBadge).toBeVisible();
});

Step 5: Run the test

terminal
npx playwright test --project="Pixel 5"

Tip: Use npx playwright test --project="iPhone 13" --headed to visually watch the test run in a resized browser window. This helps debug layout issues that only appear on specific viewports.

Step 6: Custom device profiles.

If the built-in profiles do not match your target device, define a custom one.

playwright.config.ts
{
  name: 'Custom Android Tablet',
  use: {
    viewport: { width: 800, height: 1280 },
    userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-X200) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    deviceScaleFactor: 2,
    isMobile: true,
    hasTouch: true,
  },
}

The Playwright architecture uses a client-server model where these device parameters are sent to the browser server process, which applies them before rendering any page.

Emulation vs real device testing: what actually changes?

This is the core question every team faces. Emulation is fast and free. Real device testing is accurate but costs money. The right answer depends on what you are testing.

Factor Emulation Real device testing
Speed Fast. Tests run locally, no network latency. Slower. Network round-trip to cloud device.
Cost Free. Included with Playwright. Paid. Requires cloud platform subscription.
Rendering accuracy Uses desktop browser engine. WebKit on desktop differs from Safari on iOS. Uses actual device browser with real rendering engine.
Touch/gesture fidelity Simulated. Cannot replicate multi-touch hardware behavior. Real hardware touch screen and gesture processing.
Performance metrics Misleading. Uses host machine CPU/GPU resources. Accurate. Real CPU, RAM, and GPU constraints.
Hardware features Cannot test GPS, camera, biometrics, or sensors. Full access to device hardware capabilities.
CI/CD integration Simple. No external dependencies. Requires API keys and cloud platform configuration.
Device coverage 100+ built-in profiles. Custom profiles possible. Thousands of real devices across OS versions.

Tip: A practical strategy: use emulation for 80% of mobile test runs (layout validation, responsive behavior, functional flows) and reserve real device testing for the remaining 20% (performance benchmarks, Safari-specific rendering, hardware-dependent features).

The biggest gap shows up with iOS Safari. Playwright uses WebKit on desktop to emulate Safari, but desktop WebKit is not the same engine as mobile Safari on an iPhone. Apple's mobile Safari has unique scrolling behavior, fixed positioning quirks, and viewport handling that desktop WebKit cannot replicate. Teams that skip real device validation for iOS routinely discover Safari-only bugs in production.

A common failure pattern: your emulated test on storedemo.testdino.com shows the product grid rendering in 2 columns on an iPhone 13 viewport. The test passes. But on a real iPhone 13, the grid renders in 1 column because mobile Safari interprets the CSS gap property differently when combined with flex-wrap. Emulation cannot catch this.

Here is a test that validates the product grid column count on mobile:

tests/mobile-product-grid.spec.ts
import { testexpect } from "@playwright/test";
test("product grid shows correct column layout on mobile"async ({ page }) => {
  await page.goto("/");

  // Scroll to the product section
  const productSection = page.locator(".product-card").first();
  await productSection.scrollIntoViewIfNeeded();

  // Get the viewport width
  const viewport = page.viewportSize();

  if (viewport && viewport.width < 768) {
    // On mobile, products should stack or show 2 columns max
    const firstCard = await page.locator(".product-card").first().boundingBox();
    const secondCard = await page.locator(".product-card").nth(1).boundingBox();

    if (firstCard && secondCard) {
      // If cards are stacked, second card's Y should be greater than first card's Y + height

      // If side by side, they share approximately the same Y
      const isStacked = secondCard.y > firstCard.y + firstCard.height / 2;
      const isTwoColumn = Math.abs(secondCard.y - firstCard.y) < 10;
      expect(isStacked || isTwoColumn).toBeTruthy();
    }
  }
});

Source: Perfecto's 2024 "Mobile Testing Coverage Report" comparing emulation vs physical device defect discovery across 12,000 test suites

How do you run Playwright tests on real mobile devices?

Playwright does not ship with built-in real device connectivity. To run tests on physical Android or iOS devices, you connect Playwright to a remote browser session hosted by a cloud device provider.

Here are the three main approaches.

Approach 1: Cloud SDK integration (LambdaTest example)

Most cloud providers offer an SDK or WebSocket-based connection. Here is a setup using LambdaTest as an example. The pattern is similar across providers.

Step 1: Install the provider SDK

terminal
npm install -D lambdatest-node-sdk

Step 2: Configure device capabilities

tests/real-device-mobile.spec.ts
import { testexpect } from "@playwright/test";
const capabilities = {
  browserName: "Chrome",
  browserVersion: "latest",

  "LT:Options": {
    platform: "Android",
    deviceName: "Pixel 7",
    platformVersion: "13.0",
    user: process.env.LT_USERNAME,
    accessKey: process.env.LT_ACCESS_KEY,
    network: true,
    console: true,
  },
};

test("verify product page loads on real Pixel 7"async ({ browser }) => {
  const context = await browser.newContext(capabilities);
  const page = await context.newPage();
  await page.goto("https://storedemo.testdino.com");
  await expect(page.getByText("Demo E-commerce Testing Store")).toBeVisible();

  // Navigate to products and verify grid renders
  await page.getByText("All Products").click();
  await expect(page.getByPlaceholder("Search products...")).toBeVisible();
  await context.close();
});

Approach 2: BrowserStack SDK integration

BrowserStack uses a YAML config file for device targets.

browserstack.yml
userName: YOUR_USERNAME
accessKey: YOUR_ACCESS_KEY

platforms:
  - deviceName: Samsung Galaxy S23
    osVersion: 13.0
    browserName: chrome
    browserVersion: latest
  - deviceName: iPhone 15
    osVersion: 17
    browserName: safari
    browserVersion: latest
parallelsPerPlatform: 2

terminal
npm install -D browserstack-node-sdk
npx browserstack-node-sdk playwright test

Approach 3: Experimental Android support (local USB)

Playwright has experimental support for connecting to real Android devices via ADB. No cloud subscription needed.

tests/android-local.spec.ts
import { _android as android } from "playwright";
(async () => {
  const [device] = await android.devices();
  console.log(`Connected to: ${device.model()}`);

  await device.shell("am force-stop com.android.chrome");
  const context = await device.launchBrowser();
  const page = await context.newPage();
  await page.goto("https://storedemo.testdino.com");

  // Verify the store loads on a real Android device
  const heading = page.getByText("Demo E-commerce Testing Store");
  console.log(`Heading visible: ${await heading.isVisible()}`);

  await context.close();
  await device.close();
})();

Note: The _android API is experimental (Playwright v1.20+) and requires: (1) a physical Android device connected via USB, (2) ADB daemon running, (3) USB debugging enabled, and (4) Chrome 87+ installed. Screenshots only work when the device screen is awake.

For iOS, Playwright cannot connect directly to Safari on physical iPhones from a local setup. Apple restricts third-party browser automation on iOS devices. Cloud providers are the only option for real iPhone testing.

Understanding how Playwright locators work across device contexts is critical. Locators like getByRole and getByText stay consistent whether you test on emulation or a real device.

Cloud platform comparison: choosing a real device provider

Picking a provider depends on device coverage needs, budget, and CI integration.

Feature LambdaTest BrowserStack PCloudy
Real device count 3,000+ (Android & iOS) 3,500+ (Android & iOS) 500+ (Android & iOS)
Playwright support WebSocket-based connection Native SDK integration CDP-based connection
iOS Safari on real iPhone Yes (supported) Yes (full support) Limited
Parallel execution Yes (plan-based limits) Yes (plan-based limits) Yes (limited)
Video recording Automatic Automatic Manual trigger
CI/CD integration GitHub Actions, GitLab CI, Jenkins, Azure DevOps GitHub Actions, GitLab CI, Jenkins, CircleCI Jenkins, CircleCI
Pricing Starts at $15/month Starts at $29/month Starts at $100/month
Free trial 100 minutes 100 minutes Free trial available

All three support Playwright. LambdaTest is the most budget-friendly option. BrowserStack has the widest iOS device catalog. PCloudy is a niche choice for teams already using it for Appium-based native testing.

When running Playwright parallel execution on cloud platforms, align your worker count with your subscription tier to avoid session queue bottlenecks.

Debug failed mobile tests faster
Pinpoint flaky tests with AI root cause analysis.
Get started CTA Graphic

Handling touch events and mobile gestures in Playwright

When a device profile has hasTouch: true, Playwright automatically routes pointer events as touch events. This means page.click() internally dispatches a touch event instead of a mouse event on mobile contexts.

Here are working examples against the TestDino Demo Store.

Tap to all products:

tests/mobile-navigation.spec.ts
import { testexpectdevices } from '@playwright/test';
test.use({
  ...devices['Pixel 5'],
});

test('test'async ({ page }) => {
  await page.goto('/');
  await page.getByTestId('header-menu-icon').click();
  await page.getByTestId('header-menu-all-products').nth(1).click();
});

Tip: Always set isMobile: true in your device config. This property controls whether the browser respects the <meta name="viewport"> tag. Without it, mobile-optimized pages render at desktop width even if the viewport size is set correctly.

A common debugging pitfall: page.tap() fails with "element not visible" on a mobile viewport because a sticky header covers the target element. Fix this by scrolling the element into view first:

tests/scroll-then-tap.spec.ts
await page.locator(".target-element").scrollIntoViewIfNeeded();
await page.locator(".target-element").tap();

Running mobile tests in CI/CD pipelines

Mobile emulation tests run identically in CI as they do locally. No special configuration needed because emulation uses the same browser binaries.

GitHub Actions example:

.github/workflows/mobile-tests.yml
name: Mobile Tests
on: [push, pull_request]
jobs:
  mobile-emulation:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test --project="Pixel 5" --project="iPhone 13"
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: mobile-test-report
          path: playwright-report/

  real-device-tests:
    runs-on: ubuntu-latest
    needs: mobile-emulation
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run test:cloud-devices
        env:
          CLOUD_USERNAME: ${{ secrets.CLOUD_USERNAME }}
          CLOUD_ACCESS_KEY: ${{ secrets.CLOUD_ACCESS_KEY }}

This workflow creates a two-stage pipeline.

  • Stage 1 runs tests on mobile emulation profiles. Fast, free, catches layout and functional regressions.
  • Stage 2 runs the same tests on real devices via your cloud provider. Validates rendering accuracy on actual hardware.

The needs: mobile-emulation directive ensures real device tests only run after emulation passes. This saves cloud minutes by skipping real device runs when tests are already failing.

Note: Store cloud provider credentials as encrypted secrets in your CI system. Never hardcode API keys in config files or commit them to version control.

GitLab CI example:

.gitlab-ci.yml
mobile-tests:
  image: mcr.microsoft.com/playwright:v1.49.0-noble
  stage: test
  script:
    - npm ci
    - npx playwright test --project="Pixel 5" --project="iPhone 13"
  artifacts:
    when: always
    paths:
      - playwright-report/
    expire_in: 7 days

When investigating flaky tests in mobile CI runs, pay special attention to viewport-dependent selectors and timing issues caused by mobile-specific animations.

Teams using TestDino alongside their Playwright CI setup can track mobile test pass/fail trends across devices from a single observability dashboard. This makes it easier to catch device-specific regressions before they reach production.

Playwright vs Appium vs Maestro vs Detox: picking the right mobile testing tool

A common question: should you use Playwright for mobile testing at all, or pick a dedicated mobile automation framework? Four tools dominate the landscape, and each targets a different slice of mobile testing.

Playwright vs Appium vs Maestro vs Detox

Capability Playwright Appium Maestro Detox
Primary use case Mobile web, PWA, hybrid WebView Native, hybrid, and mobile web apps Native mobile UI (iOS & Android) React Native E2E testing
Testing approach Black-box (browser automation) Black-box (WebDriver protocol) Black-box (UI-level) Gray-box (in-process, app-aware)
Language support JS/TS, Python, Java, C# Java, Python, Ruby, C#, JS YAML (declarative) JS/TS only (Jest integration)
Setup complexity Low (npm install) High (server, drivers, SDKs) Low (CLI install) Medium (native build config required)
Execution speed Fast (direct browser protocol) Moderate (WebDriver overhead) Fast (interpreted, no compilation) Very fast (in-process, same thread)
Flaky test handling Auto-wait + retry on assertion Explicit waits needed, flake-prone Intelligent UI wait, auto-retry Auto-sync with animations, network, and RN bridge
iOS real device Via cloud platforms only Yes (XCUITest driver) Yes (native support) Yes (simulators + real devices)
Android real device Experimental (_android API) + cloud Yes (UIAutomator2/Espresso) Yes (native support) Yes (emulators + real devices)
Cross-browser testing Chromium, Firefox, WebKit Limited to device browser No (app-focused) No (app-focused)
CI/CD friendliness Excellent (Docker images, GitHub Actions) Good (requires Appium server) Good (Maestro Cloud available) Excellent (built for CI, headless mode)
Learning curve Moderate Steep Low (YAML-based) Moderate (JS/TS + native config)
Community (GitHub stars) 68k+ 18k+ 7k+ 11k+

When Playwright is the right choice:

  • You are testing a responsive web app, PWA, or hybrid app with WebViews.

  • You need cross-browser coverage (Chromium + Firefox + WebKit) on mobile viewports.

  • Your team already uses Playwright for desktop web testing and wants to reuse the same framework.

  • You want fast CI/CD feedback with zero external dependencies for mobile emulation.

  • Your test suite needs to cover both desktop and mobile web flows without switching tools.

When Appium is the right choice:

  • You are testing a native Android or iOS app with complex UI interactions.

  • You need access to hardware features like GPS, camera, biometrics, or push notifications.

  • Your team works across multiple platforms and languages (Java, Python, Ruby, C#).

  • You are testing hybrid apps that mix native components with WebViews.

  • Your organization has invested in the Selenium/WebDriver ecosystem and wants to stay on that standard.

When Maestro is the right choice:

  • You want fast, no-code mobile UI validation using YAML.

  • Your team includes QA engineers or product managers who are not comfortable writing JavaScript.

  • You are testing Flutter, React Native, or SwiftUI apps where visual test creation speeds up coverage.

  • You need to spin up quick smoke tests without configuring build systems.

  • You want cloud-hosted test orchestration with Maestro Cloud for parallel runs.

When Detox is the right choice:

  • Your product is built on React Native and you need E2E tests tightly coupled to the app lifecycle.

  • Test flakiness is a major issue because your app uses heavy animations, complex async operations, or network-dependent flows.

  • Your team writes JavaScript/TypeScript and wants a testing framework that integrates natively with Jest.

  • You need tests that automatically wait for the React Native bridge, animations, and network calls to settle before each action.

  • CI reliability matters more than language flexibility. Detox's gray-box approach produces some of the most deterministic test results in the mobile testing space.

A practical combination that many teams use: Playwright for mobile web testing, Detox for React Native E2E coverage, and a cloud provider for real device validation of critical flows. This covers the full spectrum without forcing any single tool to do everything.

According to the Appium market share data, Appium remains the most widely adopted mobile testing framework as of 2026. However, Detox adoption has grown steadily among React Native teams, and Playwright is increasingly chosen by teams that primarily test mobile web experiences.

Source: Based on JetBrains "State of Developer Ecosystem" surveys 2022-2024 and Stack Overflow Developer Surveys 2022-2025 adoption trend data.

Ship mobile-ready apps confidently
Monitor Playwright test health across all device configs.
Start free CTA Graphic

Conclusion

Playwright mobile testing gives you two paths: emulation for fast, free validation of layouts and functional flows, and real device clouds for Safari accuracy and hardware-level performance. Use the built-in devices API for emulation and a cloud SDK for real devices. The same test code runs on both, and CI integration needs nothing more than a YAML config and API credentials.

For responsive web apps, PWAs, or hybrid apps with WebViews, Playwright covers your mobile testing needs without forcing you into a separate framework. For native mobile apps, pair it with Appium, Maestro, or Detox depending on your stack and complexity.

Start with emulation. Add real devices for your most critical user flows. Build a BDD-driven test structure so your suite stays maintainable as device targets grow, and use TestDino to track pass/fail rates across every config from one dashboard.

Your mobile users will not wait for a broken experience to be fixed. Test it before they see it.

FAQs

Can Playwright test on real mobile devices?
Yes. Playwright supports device emulation natively, and you can run tests on real physical devices through cloud providers like LambdaTest, BrowserStack, or PCloudy. These platforms expose real Android and iOS devices over remote WebSocket (CDP) connections. For Android, Playwright also has an experimental _android API for local USB connections.
What is the difference between Playwright mobile emulation and real device testing?
Emulation spoofs the user agent, viewport, touch support, and device scale factor inside a desktop browser engine. It is fast and free but cannot replicate hardware-level performance, Safari's actual iOS rendering engine, or sensors like GPS and Face ID. Real device testing runs on physical hardware, catching issues that emulation misses.
How do I set up Playwright mobile testing for Android?
For emulation, add a project in playwright.config.ts using a device profile like Pixel 5 or Galaxy S9+. For real Android devices, use the experimental _android API with a USB-connected device, or connect to a cloud provider. The same test code works in both modes.
Does Playwright support iOS Safari testing on real iPhones?
Playwright cannot connect directly to Safari on physical iPhones locally. Apple restricts third-party browser automation. However, LambdaTest and BrowserStack both support Playwright tests on real iPhones and iPads running Safari, enabling validation of iOS-specific rendering and scrolling behavior.
How do I handle touch events in Playwright mobile tests?
When using a device profile with hasTouch: true, Playwright routes pointer events as touch events automatically. Use page.tap() or page.touchscreen.tap() for taps, combine with mouse.move for swipes. Set isMobile: true so the browser respects the viewport meta tag.
Ayush Mania

Forward Development Engineer

Ayush Mania is a Forward Development Engineer at TestDino, focusing on platform infrastructure, CI workflows, and reliability engineering. His work involves building systems that improve debugging, failure detection, and overall test stability.

He contributes to architecture design, automation pipelines, and quality engineering practices that help teams run efficient development and testing workflows.

Get started fast

Step-by-step guides, real-world examples, and proven strategies to maximize your test reporting success