Guide
Mocking API Responses with Puppeteer Request Interception in Automated Tests
Learn how to mock a specific API call in Puppeteer using request interception to achieve deterministic UI tests without changing the backend.
Published by Tasadduq Burney
14 May 2026, 16:09 UTC
3 min39.6K views0

Desired outcome
Replace a specific network request (for example, GET /api/data) with a custom JSON payload while keeping the rest of the application unchanged. This lets you drive deterministic UI tests without modifying the backend or relying on a test double server.
Prerequisites
- Node.js version 14 or newer.
- Puppeteer version 10 or newer installed via npm (
npm i puppeteer). - A test script that launches Puppeteer (headless or headed) and navigates to the page under test.
- Basic familiarity with asynchronous JavaScript (
async/await).
Procedure
- Launch the browser and create a page.
const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch({ headless: false }); // set true for CI const page = await browser.newPage(); // … - Enable request interception. This must be done before any navigation that could trigger the request you intend to mock.
await page.setRequestInterception(true); - Attach a listener to the
requestevent. Inside the handler, inspect the URL; if it matches the target endpoint, fabricate a response; otherwise, let the request proceed normally.const mockData = { id: 42, message: 'mocked payload' }; page.on('request', (request) => { const url = request.url(); if (url.endsWith('/api/data')) { // Respond with a 200 OK and custom JSON body request.respond({ status: 200, contentType: 'application/json', body: JSON.stringify(mockData) }); } else { request.continue(); } }); - Navigate to the test page. Because interception is already active, the first request to
/api/datawill be served by the mock.await page.goto('https://example-app.com/dashboard', { waitUntil: 'networkidle0' }); - Assert that the UI reflects the mocked data. Use
page.evaluateto run code in the page context and verify the result.const retrieved = await page.evaluate(() => { return fetch('/api/data').then(r => r.json()); }); console.log('Received:', retrieved); // should match mockData // Example assertion (using a test framework like Jest): // expect(retrieved).toEqual(mockData); - Clean up after the test. Disable interception to avoid leaking state into subsequent tests.
await page.setRequestInterception(false); await browser.close();
Expected checks
- Log the mocked response body (as shown above) and confirm it equals
mockData. - In headed mode, open Chrome DevTools → Network tab; you should see the request to
/api/datareturn status 200 with the custom JSON, while other requests (e.g., assets, unrelated APIs) show normal server responses. - After disabling interception, make a request to a different endpoint (e.g.,
/api/health) and verify it returns the actual server response, not the mock.
Recovery options and limitations
- Timing risk. If you enable interception after a navigation that already triggered
/api/data, the original request will slip through. EnsuresetRequestInterception(true)is called immediately after creating the page and before anygotoorwaitForNavigation. - Performance impact. Each intercepted request incurs an extra callback; limit interception to the specific URLs you need to mock. Use a precise string match or a regular expression to avoid unnecessary overhead.
- Scope. Request interception works for HTTP(S) requests initiated by the page (XHR, fetch, iframe, etc.). It does not intercept WebSocket frames or service‑worker fetches; those require different approaches.
- Verification practicality. After the test, run a quick sanity check:
await page.evaluate(() => fetch('/api/health').then(r => r.status))should return the real server’s status code (e.g., 200). If it returns your mocked payload, interception was not disabled correctly.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.