Q18 of 42 · Playwright
How do you intercept and mock network requests in Playwright?
Short answer
Short answer: Use `page.route(pattern, handler)` to intercept matching requests. The handler can `fulfill` (mock the response), `continue` (pass through), or `abort`. Set up before navigation. `context.route` applies across all pages in the context.
Detail
page.route is the core API — same idea as Cypress's cy.intercept but with a function-handler shape:
await page.route('**/api/cart', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ items: [], total: 0 }),
});
});
await page.goto('/cart');
Handler options:
route.fulfill({ ... })— return a mock response.route.continue({ ... })— pass through, optionally modifying headers/post data/URL.route.abort()— fail the request (test offline / error states).route.fetch()— fetch the real response, modify it, then fulfill.
Pattern matching:
- Glob:
'**/api/cart'— Playwright's micromatch. - Regex:
/\/api\/cart\?.*/. - Function:
(url) => url.pathname === '/api/cart'.
Per-context vs per-page:
page.route— only that page.context.route— all pages in the context.
Order of registration matters. Route handlers register in registration order; the first match handles the request. To unregister: await page.unroute(pattern).
Useful patterns:
// Modify a real response (fetch + fulfill)
await page.route('**/api/cart', async (route) => {
const response = await route.fetch();
const body = await response.json();
body.total = 99.99; // tweak for test
await route.fulfill({ response, json: body });
});
// Simulate network error
await page.route('**/api/cart', (route) => route.abort('failed'));
// Spy without modifying
const requests: Request[] = [];
page.on('request', (req) => {
if (req.url().includes('/api/cart')) requests.push(req);
});
The senior signal: knowing the four route actions, the order-of-registration rule, and the spy-via-event approach for non-modifying observation.
// EXAMPLE
intercept.spec.ts
import { test, expect } from '@playwright/test';
test('renders empty cart from mocked response', async ({ page }) => {
await page.route('**/api/cart', (route) =>
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ items: [], total: 0 }),
}),
);
await page.goto('/cart');
await expect(page.getByTestId('empty-state')).toBeVisible();
});
test('shows error state when API fails', async ({ page }) => {
await page.route('**/api/cart', (route) =>
route.fulfill({ status: 500, body: 'Server error' }),
);
await page.goto('/cart');
await expect(page.getByTestId('error')).toContainText('try again');
});