Frontend tests often need one backend response to stay fixed while the rest of the page runs normally. In Playwright, a route mock can replace a matching API response so loading, empty-state, and error UI can be tested without depending on the backend's current data.

The page.route handler must be registered before the page action that triggers the request. route.fulfill() sends the status, headers, and body that the browser page receives, while unrelated requests continue to the real application.

Use the narrowest URL pattern that covers the endpoint under test, then assert something visible in the page from the mocked payload. If a service worker or Mock Service Worker already controls the request, block service workers for the test project so Playwright can see and fulfill the request.

Steps to mock network responses in Playwright:

  1. Open the existing Playwright project.
    $ cd ~/projects/playwright-demo

    Install Playwright first when the project does not already have @playwright/test.
    Related: How to install Playwright with npm

  2. Confirm that the project has a base URL for the application page.
    playwright.config.ts
    import { defineConfig } from '@playwright/test';
     
    export default defineConfig({
      use: {
        baseURL: process.env.APP_BASE_URL ?? 'http://127.0.0.1:4173',
      },
    });

    If playwright.config.ts already has a use block, add only the baseURL line and keep existing browser, trace, timeout, and project options.
    Related: How to set base URL in Playwright tests

  3. Create tests/network-mock.spec.ts with a route that fulfills the API response before navigation.
    tests/network-mock.spec.ts
    import { test, expect } from '@playwright/test';
     
    test('status page renders mocked API state', async ({ page }) => {
      const interceptedUrls: string[] = [];
     
      await page.route('**/api/status', async route => {
        interceptedUrls.push(route.request().url());
     
        await route.fulfill({
          status: 200,
          json: {
            status: 'ok',
            message: 'Mocked by Playwright',
          },
        });
      });
     
      await page.goto('/status');
     
      await expect(page.getByRole('heading', { name: 'Mocked by Playwright' })).toBeVisible();
      await expect(page.getByText('Status: ok')).toBeVisible();
      expect(interceptedUrls).toHaveLength(1);
    });

    The route pattern should match only the API endpoint being controlled. Register the route before page.goto(), a click, or any other action that triggers the request.

  4. Run the mock response spec against the application URL.
    $ APP_BASE_URL=http://127.0.0.1:4173 npx playwright test tests/network-mock.spec.ts
    
    Running 1 test using 1 worker
    
      ✓  1 [chromium] › tests/network-mock.spec.ts:3:5 › status page renders mocked API state (117ms)
    
      1 passed (1.2s)

    Replace the sample APP_BASE_URL with the page that normally calls the real endpoint. A passing run confirms that the route handled the request once and the UI rendered the mocked JSON.