Playwright Network Mocking & API Testing

End-to-end tests are most valuable when they exercise the real stack, but sometimes you need control over the network: simulating a payment provider's error, testing an empty state without seeding data, freezing a flaky third-party widget, or checking how the UI behaves when an API is slow. Playwright intercepts every request a page makes, and lets tests fulfill them with mock data, modify real responses, abort them, or delay them.

The same toolkit includes HAR recording and replay for deterministic snapshots of backend traffic, and a built-in request fixture for direct API testing, which is also ideal for creating test data and verifying backend state alongside UI tests.

TL;DR

Quick Example

Core Concepts

Routing

page.route(url, handler) registers an interceptor for matching requests. URL patterns can be globs (/api/), regexes, or predicate functions. context.route applies to every page in the context, including popups and new tabs. Handlers must resolve each route exactly once:

Remove routes with page.unroute, or register them in fixtures scoped to specific tests.

Simulating Failure Modes

Resilience UI is hard to test against real backends. With routing you can simulate:

Blocking Third Parties

Analytics, chat widgets, ads, and A/B testing scripts slow tests and add flakiness. Block them globally in a fixture (context.route(/googletagmanager|hotjar/, r => r.abort())), or stub them with minimal responses.

HAR Record and Replay

A HAR (HTTP Archive) file captures request and response pairs:

It's useful for frontend tests needing realistic data without a live backend, and for isolating UI changes from backend variability. Refresh HARs when APIs change, since stale HARs silently test outdated contracts.

Waiting on Network Events

Always create the wait promise before the action that triggers the request, to avoid races. page.on('request') and page.on('response') listeners help assert that no unexpected calls happen.

API Testing With request

The request fixture (an APIRequestContext) sends HTTP requests with the config's baseURL, shared auth storage state, and cookies:

When to Mock vs Use the Real Backend

A healthy suite does both: a smaller set of true E2E journeys against real services, plus mocked tests covering UI edge cases. Keep mocks aligned with real API schemas, generated from OpenAPI types or validated against contracts. See mocking and stubbing.

Best Practices

Scope Mocks Tightly

Match the specific endpoint and method you intend to mock. Broad patterns (**/*) can accidentally intercept assets, other APIs, or auth calls, causing confusing failures.

Type Mock Payloads

Build mock responses from shared TypeScript types or factories, so API changes break mocks at compile time instead of letting tests pass against outdated shapes.

Assert on Requests Too

When behavior depends on what the UI sends (query parameters, payload shape, headers), inspect route.request() or the result of waitForRequest, and assert on it.

Don't Mock What You're Testing

If the goal is to verify checkout end to end, mocking the order API defeats the purpose. Mock only boundaries outside the test's scope.

Common Mistakes

Registering Routes After Navigation

Register routes before the action that triggers the request.

Starting waitForResponse After the Click

Create the promise first, then click, then await it.

Stale HAR Files

Replaying months-old recordings hides breaking API changes. Regenerate HARs as part of API change workflows, or validate them against current schemas.

FAQ

How do I mock an API response in Playwright?

Use page.route('**/api/endpoint', route => route.fulfill({ status: 200, json: data })) before triggering the request. For partial changes, call route.fetch() to get the real response and fulfill with modified JSON.

Can Playwright be used for API testing without a browser?

Yes. The request fixture (or playwright.request.newContext()) sends HTTP requests directly, with assertions via expect. It's fast and shares configuration and authentication with UI tests.

How do I simulate a slow network?

Delay inside a route handler before fulfilling or continuing (for example await new Promise(r => setTimeout(r, 3000))) to test loading states. For full throttling in Chromium, CDP network emulation is available via context.newCDPSession.

What is HAR replay good for?

Deterministic frontend tests using realistic recorded backend responses, without running or seeding the backend. It's useful for UI-focused suites and for reproducing specific data scenarios. Keep recordings fresh so they match current APIs.

Related Topics

References