Playwright·Cookbook Field Manual
Part 01 · Network Mocking Card 04

Mock Only What You Need

Card 04: Mock Only What You Need

What This Pattern Solves

When you mock APIs, it’s easy to accidentally let unmocked requests slip through to the real network. This causes flaky tests, unexpected external calls in CI, and hard-to-debug failures. You need a way to ensure only the endpoints you explicitly mock are called—everything else should fail fast.

How It Works

  1. Register a fallback route with a broad pattern (e.g., **/swapi.dev/**) that aborts or fails
  2. Register specific routes for the endpoints you want to mock
  3. Playwright’s route order (last registered = first matched) means specific routes run first
  4. Any unhandled request hits the fallback and fails the test
  5. Optionally track unhandled requests in an array to assert zero at the end

This is strict mode for API mocking—nothing gets through unless you explicitly allow it.

Code Example

import { test, expect } from '@playwright/test';
import type { SwapiPerson } from '../swapi/schema';

test.describe('04-mock-only-what-you-need: Strict mock scope', () => {
  test('only people/1 is mocked; other SWAPI requests are aborted', async ({ page }) => {
    const luke = {
      name: 'Luke Skywalker',
      height: '172',
    } satisfies Partial<SwapiPerson>;

    const unhandled: string[] = [];

    // 1. Fallback catches everything (registered FIRST)
    await page.route('**/swapi.dev/**', async (route) => {
      unhandled.push(route.request().url());
      await route.abort('blockedbyclient');
    });

    // 2. Specific mock (registered LAST, runs FIRST due to Playwright order)
    await page.route('**/swapi.dev/api/people/1/**', (route) =>
      route.fulfill({ json: luke }),
    );

    await page.goto('/cards/04');

    // 3. Assert no unhandled requests
    await expect(page.getByTestId('person-name')).toHaveText('Luke Skywalker');
    expect(unhandled).toHaveLength(0);
  });
});

The real spec also covers context.route (mock applies to every page in the context), route.fallback() (delegate to the next matching handler), and the times option (handle only the first N requests).

Run This Example

pnpm test src/04-mock-only-what-you-need

Prerequisites

Key Concepts

When to Use This Pattern

Common Mistakes

  1. Wrong route order (specific route registered first):

    // ❌ WRONG - specific route won't run (fallback runs first)
    await page.route('**/people/1/**', mockHandler);
    await page.route('**/swapi.dev/**', abortHandler);
    
    // ✓ CORRECT - fallback first, specific last
    await page.route('**/swapi.dev/**', abortHandler);
    await page.route('**/people/1/**', mockHandler);
  2. Forgetting about other requests:

    • Pages often load CSS, fonts, images, analytics
    • Either mock them too, or use a more specific fallback pattern like **/api/**
  3. Aborting with wrong error type:

    • Use 'blockedbyclient' for intentional blocks
    • Page may show network error if you use 'failed'
  4. Not checking unhandled array:

    // ❌ WRONG - forgot to assert
    const unhandled: string[] = [];
    await page.route('**/*', (route) => unhandled.push(route.request().url()));
    // Test passes even with unhandled requests!
    
    // ✓ CORRECT - always assert
    expect(unhandled).toHaveLength(0);

Flow Diagram

sequenceDiagram
    participant Test
    participant Page
    participant Specific Route
    participant Fallback Route

    Test->>Page: route('**/swapi.dev/**', abort) [registered first]
    Test->>Page: route('**/people/1/**', mock) [registered last]
    Note over Page: Last registered = first matched

    Test->>Page: goto('/cards/04')
    Page->>Specific Route: fetch('/people/1/')
    Note over Specific Route: Pattern matches!
    Specific Route-->>Page: 200 + mock data ✓

    Page->>Fallback Route: fetch('/people/2/') [unmocked!]
    Note over Fallback Route: No specific route matched
    Fallback Route->>Fallback Route: abort('blockedbyclient')
    Fallback Route->>Test: Track in unhandled[]

    Test->>Test: expect(unhandled).toHaveLength(0)
    Note over Test: Test FAILS - caught unmocked request!

Live Demo

👇 Test uses strict mode to catch unhandled requests:

Loading…

Run This Example

pnpm test src/04-mock-only-what-you-need