Playwright Network Interception and Mock

This chapter introduces how to use Playwright to intercept, modify, and mock network requests, enabling independent testing that does not rely on backend APIs.


What is Network Interception

Network interception allows you to intervene when the browser makes HTTP requests.

You canblock requests, modify responses, and return mock data, making tests independent of external APIs or network status.


page.route() Intercept Requests

page.route(url, handler)Used to intercept all network requests matching a specified URL pattern.

Basic Interception

Example

test('Intercept all image requests', async ({ page }) => {
  // Intercept all PNG and JPEG images
  await page.route('**/*.{png,jpg,jpeg}', route => route.abort());

  // Navigate to the page (images will not be loaded)
  await page.goto('https://www.example.com/');
  // The page may load faster because no images are downloaded
});

URL Matching PatternsSupports the following formats:

PatternExampleDescription
Full URL'https://api.example.com/users'Exact match
Glob wildcard'**/api/**'**Matches any path
Regular expression/\/api\/v\d\/users/Flexible matching
Functionurl => url.includes('/api/')Programmatic logic matching

route.abort() Abort Requests

Example

test('Block third-party analytics scripts', async ({ page }) => {
  // Block analytics scripts such as Google Analytics
  await page.route('**/*analytics*', route => route.abort());
  // Block CSS files
  await page.route('**/*.css', route => route.abort());

  await page.goto('https://www.example.com/');
  // The page loads without styles, but functional tests are unaffected
});

route.fulfill() Mock Responses

route.fulfill()It is the most powerful mock method, capable of returning any custom response.

Mock JSON Data

Example

test('Mock user list API', async ({ page }) => {
  // Intercept the user list API and return mock data
  await page.route('**/api/users', async route => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      json: [
        { id: 1, name: 'example_user', email: '[email protected]' },
        { id: 2, name: 'test_user', email: '[email protected]' },
      ],
    });
  });

  await page.goto('https://www.example.com/users');
  // The page will display mock data instead of real API data
});

Mock Error Responses

Example

test('Test API error handling', async ({ page }) => {
  // Simulate a 500 server error
  await page.route('**/api/data', async route => {
    await route.fulfill({
      status: 500,
      contentType: 'application/json',
      json: { error: 'Internal server error' },
    });
  });

  await page.goto('https://www.example.com/dashboard');
  // Verify the error message is displayed correctly
  await expect(page.getByText('Internal server error')).toBeVisible();
});

route.continue() Modify and Continue

Rather than directly returning mock data, it modifies the real request or response and then continues sending it to the server.

Example

test('Modify request headers', async ({ page }) => {
  await page.route('**/api/**', async (route, request) => {
    // Modify request headers and add custom headers
    const headers = {
      ...request.headers(),
      'X-Custom-Header': 'EXAMPLE-TEST',
    };
    await route.continue({ headers });
  });

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

test('Modify POST data', async ({ page }) => {
  await page.route('**/api/login', async (route, request) => {
    // Override the body of POST requests
    const postData = JSON.parse(request.postData() || '{}');
    postData.mode = 'test';  // Add a test marker
    await route.continue({
      postData: JSON.stringify(postData),
    });
  });
});

route.fetch() Real Requests

In the mock handler function, you can useroute.fetch()to actually send the request, get the real response, and then modify it.

Example

test('Modify the real response', async ({ page }) => {
  await page.route('**/api/products', async route => {
    // First get the real response
    const response = await route.fetch();
    const body = await response.json();

    // Modify the response data
    body.items[0].name = 'EXAMPLE modified product name';

    // Return the modified data
    await route.fulfill({
      status: response.status(),
      contentType: 'application/json',
      json: body,
    });
  });
});

Context-level Interception vs Page-level Interception

MethodScopeUse Cases
page.route()Current page onlyNetwork mock for a single page
context.route()All pages in this contextMock rules shared by multiple pages

Example

test.beforeEach(async ({ context }) => {
  // Intercept in the entire context; applies to all pages
  await context.route('**/*.css', route => route.abort());
});

test('Test 1', async ({ page }) => { /* No CSS */ });
test('Test 2', async ({ page }) => { /* No CSS */ });

HAR File Recording and Playback

A HAR (HTTP Archive) file records all network requests of a page and can then replay or mock them.

Example

test('Use HAR file for mocking', async ({ page }) => {
  // Load network records from the HAR file and replay
  await page.routeFromHAR('tests/hars/api-responses.har');

  await page.goto('https://www.example.com/');
  // All matching requests will get responses from the HAR file
});
# 录制 HAR 文件
npx playwright open --save-har=tests/hars/api-responses.har https://www.example.com/

HAR files are suitable for mocking large or complex API responses. Record once, and subsequent tests replay from the HAR without depending on a real server.

Other Extensions