How to Intercept and Modify Network Responses with Playwright

When testing web applications, we often depend on external APIs or backend services. If these services are down, slow, or return data we can’t control, our tests fail.

Instead of relying on a real backend for every scenario, you can use Playwright to intercept network requests and modify the responses before they even reach your frontend.

What Is It?

Playwright allows you to “hook” into the browser’s network layer. Using the page.route method, you can catch a request, fetch the real data from the server, change it (patch it), and then give the modified version back to the application.

It’s like having a proxy sitting inside your test that can rewrite data in real-time.


Why QA Teams Should Use It

In enterprise projects, this is useful for:

  • Testing Edge Cases: Force a specific field to be empty or an extremely long string to see if the UI breaks.
  • Simulating Issues: Change a 200 OK response to a 500 Internal Server Error to test your app’s error handling.
  • Dynamic Tweaks: Modify specific values (like a user’s role or a feature flag) without needing to change the database.
  • Debugging: If you suspect a UI bug is caused by certain data, you can “fake” that data quickly to confirm.

Installation & Setup

You don’t need any extra plugins. Everything is built into @playwright/test.

If you are starting fresh:

npm init playwright@latest

Practical Implementation Example

Let’s say you want to test how your page looks when a specific prefix is added to the page title, but you don’t want to change the actual HTML file on the server.

Here is how you intercept a request for title.html, fetch the real content, modify it, and fulfill the request.

import { test, expect } from '@playwright/test';

test('Modify API response on the fly', async ({ page }) => {
  // 1. Set up the interceptor before navigating
  await page.route('**/title.html', async (route) => {
    
    // 2. Fetch the real response from the server
    const response = await route.fetch();
    
    // 3. Get the original body as text
    let body = await response.text();
    
    // 4. Modify the content (e.g., inject a prefix into the title tag)
    body = body.replace('<title>', '<title>QA_DEBUG: ');

    // 5. Fulfill the request with our modified body
    await route.fulfill({
      response,
      body,
      headers: {
        ...response.headers(),
        'content-type': 'text/html'
      }
    });
  });

  // 6. Navigate to the page
  await page.goto('https://your-site.com/title.html');

  // 7. Verify the modification
  await expect(page).toHaveTitle(/QA_DEBUG:/);
});

What happened here?

  • route.fetch() calls the actual server.
  • body.replace() changes the data in memory.
  • route.fulfill() sends the “fake” data to the browser. The browser thinks this came directly from the server.

Advantages

  • Stability: Your tests will be fail-safe even if the backend data changed.
  • Speed: Fetching and modifying a response is usually faster than setting up complex state in a database.
  • No Code Changes: You don’t need to ask developers to add “test modes” to the application code.
  • Use it when: You need to test UI reactions to specific, hard-to-trigger data scenarios.

Conclusion

Modifying responses on the fly is one of the most powerful features in Playwright. It moves you away from “flaky” tests and gives you full control over the environment. Instead of waiting for the backend team to build a specific scenario for you, you can just script it yourself in seconds.

Scroll to Top