Mocking APIs with Puppeteer Request Interception
Use Puppeteer’s request interception to replace real API calls with mock responses, making end‑to‑end tests stable and fast. The post walks through a concrete example, highlights trade‑offs, and offers best‑practice tips.
05 Oct 2026, 12:57 UTC

Flaky End‑to‑End Tests: The Real Problem
When a page’s functionality depends on live API endpoints, a single network hiccup can cause a test to fail. In a continuous‑integration pipeline, that one failure can cascade into a false alarm, wasting time and resources. The goal is to isolate the UI from external services without sacrificing realism.
Why Puppeteer’s Request Interception Is a Game‑Changer
Puppeteer exposes page.setRequestInterception(true) which, when enabled before navigation, hooks every network request that the page initiates. The request event gives you the full URL, method, headers, and body, so you can decide what to do with each call:
request.respond()– inject a custom HTTP response.request.continue()– let the request go through unchanged.request.abort()– cancel the request.
With this control you can replace real API calls with deterministic mock data, ensuring that your tests run the same way every time.
Step‑by‑Step Example
Below is a minimal script that demonstrates mocking a JSON API while allowing other resources to load normally. The example assumes you’re running Node ≥ 18 and have Puppeteer installed.
const puppeteer = require('puppeteer');
(async () => {
// 1. Launch Chromium (or a Chromium‑based fork).
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
// 2. Enable interception before any navigation.
await page.setRequestInterception(true);
// 3. Register the interception handler.
page.on('request', async (req) => {
const url = req.url();
// Mock only the specific API endpoint.
if (url.includes('/api/data')) {
// 4. Respond with a custom JSON body.
await req.respond({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ id: 1, value: 'mock' })
});
} else {
// 5. Let everything else continue.
await req.continue();
}
});
// 6. Navigate to a page that fetches /api/data.
await page.goto('https://example.com');
// 7. Verify that the mock data is used.
const result = await page.evaluate(() => {
// Assume the page writes the fetched data to an element with id "output".
return document.getElementById('output')?.textContent;
});
console.log('Fetched data:', result);
await browser.close();
})();
**Where to run**: The script runs in Node, not inside the browser. It requires --headless or a visible browser for debugging. Permissions are standard Node file system and network access.
**Expected outcome**: The console should log Fetched data: { id: 1, value: 'mock' }. All other resources (images, CSS, third‑party scripts) should load normally, which you can confirm by inspecting the network panel in DevTools or by logging the URLs in the handler.
Trade‑offs and Limitations
- Chromium only: Request interception is fully supported only on Chromium‑based browsers. Firefox support is experimental and may not honor
request.respond()correctly. - Performance hit: Every intercepted request triggers a round‑trip to the Node process. For pages with hundreds of requests, this can add noticeable latency.
- Risk of breaking third‑party scripts: Enabling interception globally (i.e., without filtering) can inadvertently block or alter scripts that the page relies on, leading to silent failures.
- Header handling: A mock response must include appropriate headers (e.g.,
Content-Type) and status codes; otherwise the page may misinterpret the payload.
Best‑Practice Tips
- Enable early: Call
page.setRequestInterception(true)beforepage.goto()or any script that might trigger requests. - Filter precisely: Use
req.url()andreq.method()to narrow the scope to the endpoints you want to mock. - Always continue or respond: Never leave a request hanging; it will cause the page to stall.
- Validate headers: When mocking, set
contentTypeand any other headers the page expects. - Use a helper library: For complex mocking, consider
puppeteer-request-mockor similar wrappers that simplify the API.
Actionable Closing
Integrate request interception into your test suite to replace flaky API dependencies with deterministic mocks. Start by adding the interception logic to a single test, verify that the mock data appears, and then expand to other endpoints. Keep an eye on performance and test coverage to ensure that the benefits outweigh the overhead.
By intercepting requests, you gain full control over the network layer, making your end‑to‑end tests reliable, repeatable, and fast.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.