Modern web applications are powered by APIs and network requests fetching data, submitting forms, or validating users. When testing such applications, relying on live APIs can make tests slow, unreliable, and dependent on external systems.
That’s where network interception and mocking in Playwright come in. They let you control, inspect, and simulate network traffic allowing you to test your frontend logic without waiting for real servers.
In this chapter, we’ll cover everything from basic request interception to advanced mocking techniques including conditional mocks, simulating failures, and even faking offline mode.
1. What is network interception?
Network interception allows you to capture and modify network requests and responses made by the browser during a test.
With Playwright, you can:
- Inspect outgoing requests.
- Modify requests before they’re sent.
- Mock or replace responses.
- Block or delay certain requests.
This gives you complete control over how your app behaves under different network conditions.
2. Intercepting and logging requests
You can listen to all network requests using page.on('request').
Example:
import { test } from '@playwright/test';
test('log all network requests', async ({ page }) => {
page.on('request', request => {
console.log('➡️', request.method(), request.url());
});
page.on('response', response => {
console.log('⬅️', response.status(), response.url());
});
await page.goto('https://playwright.dev');
});
This is useful for debugging API activity or verifying the network behavior of your app.
3. Blocking resources
You can block certain file types (like images, CSS, or analytics scripts) to speed up tests.
Example:
await page.route('**/*.{png,jpg,jpeg,svg}', route => route.abort());
await page.goto('https://example.com');
This saves bandwidth and speeds up tests when visual assets aren’t needed.
Pro Tip: Use this in CI pipelines to make tests faster and more deterministic.
4. Mocking network responses
The real power of Playwright comes from mocking responses allowing you to simulate APIs.
Example: Mocking an API endpoint
import { test, expect } from '@playwright/test';
test('mock user data API', async ({ page }) => {
await page.route('**/api/users', async route => {
const mockResponse = {
status: 200,
contentType: 'application/json',
body: JSON.stringify({ id: 1, name: 'John Doe', role: 'Tester' })
};
await route.fulfill(mockResponse);
});
await page.goto('https://myapp.test/users');
const user = page.locator('#user-name');
await expect(user).toHaveText('John Doe');
});
Here, instead of hitting a real API, we intercepted the request and sent back fake data instantly.
5. Modifying existing requests
You can also modify requests before they’re sent for example, to add headers or change payloads.
Example:
await page.route('**/api/login', route => {
const request = route.request();
const headers = {
...request.headers(),
Authorization: 'Bearer fake-token'
};
route.continue({ headers });
});
This is useful for testing different authentication states or permissions.
6. Conditional mocking
Sometimes, you only want to mock certain requests under specific conditions such as for specific test cases.
Example:
await page.route('**/api/products', async route => {
const request = route.request();
if (request.url().includes('category=shoes')) {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([{ id: 1, name: 'Running Shoes' }])
});
} else {
await route.continue();
}
});
This gives you flexibility to partially mock APIs while letting others behave normally.
7. Simulating slow networks or timeouts
To test loading indicators or error handling, you can introduce delays in responses.
Example:
await page.route('**/api/data', async route => {
await new Promise(r => setTimeout(r, 3000)); // Simulate delay
await route.fulfill({
status: 200,
body: JSON.stringify({ message: 'Delayed response' })
});
});
This helps you test spinners, progress bars, or timeouts.
8. Simulating failed or offline requests
You can simulate API errors by returning specific HTTP status codes.
Example:
await page.route('**/api/login', async route => {
await route.fulfill({
status: 500,
contentType: 'application/json',
body: JSON.stringify({ error: 'Internal Server Error' })
});
});
Or simulate offline mode entirely:
await page.context().setOffline(true);
await page.goto('https://example.com');
This is great for testing apps’ offline capabilities or error handling.
9. Capturing and asserting network requests
You can capture network requests made by your app and assert on them.
Example:
const [response] = await Promise.all([
page.waitForResponse('**/api/login'),
page.click('#login-button')
]);
const body = await response.json();
expect(body).toHaveProperty('token');
This allows you to verify backend responses during end-to-end tests.
10. Recording and replaying network traffic
Playwright can record network traffic to replay later helpful when mocking large sets of data.
Record mode:
npx playwright open --save-har=network.har https://example.com
Replay mode:
await context.routeFromHAR('network.har');
await page.goto('https://example.com');
HAR files (HTTP Archive) capture all network requests and responses for future playback.
11. Combining mocks with fixtures
You can integrate network mocks with fixtures for reusable setups.
Example:
fixtures/mockApi.js:
import { test as base } from '@playwright/test';
export const test = base.extend({
mockAPI: async ({ page }, use) => {
await page.route('**/api/users', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([{ id: 1, name: 'Fixture User' }])
});
});
await use(page);
}
});
Now any test using this fixture automatically uses mocked data.
12. Debugging network interceptions
Playwright provides powerful debugging tools for network testing:
- Use
page.on('requestfailed')to catch failed requests. - Run tests in debug mode:
npx playwright test --debug - Use the Trace Viewer to inspect network logs visually:
npx playwright show-trace trace.zip
Best Practices
- Use mocking for consistent, isolated tests.
- Avoid hitting real APIs during CI, use
page.route()mocks. - Block large assets (images, fonts) for faster tests.
- Use conditional mocks to simulate realistic behavior.
- Record and replay HAR files for integration scenarios.
- Combine network mocks with fixtures for reusable test setups.
- Always test error states not just successful responses.
Exercise
Task 1: Log all network requests made on a page.
Task 2: Block all image requests to speed up tests.
Task 3: Mock an API response to simulate user data.
Task 4: Simulate a 500 error and test your app’s error handling.
Bonus: Record and replay network activity using a HAR file.
Cheat Sheet
| Action | Method |
|---|---|
| Intercept requests | page.route('**/*', handler) |
| Mock response | route.fulfill({ status, body }) |
| Modify request | route.continue({ headers }) |
| Wait for API | page.waitForResponse('**/api/**') |
| Block resources | route.abort() |
| Simulate delay | setTimeout() in handler |
| Offline mode | context.setOffline(true) |
| Record HAR | --save-har=network.har |
| Replay HAR | context.routeFromHAR('file.har') |
Summary
In this chapter, you learned how to:
- Intercept and log network requests.
- Block, modify, or mock responses.
- Simulate network delays, failures, and offline mode.
- Record and replay traffic with HAR files.
- Combine mocks with Playwright fixtures.
Mastering network interception and mocking allows you to create fast, isolated, and deterministic tests, critical for reliable end-to-end automation.
