Network Interception and Mocking
By Syncfusion®, Inc. · Last updated
Watching requests and responses, waiting for a call with waitForRequest and waitForResponse, intercepting calls with page.route, URL patterns, mocking with route.fulfill, blocking with route.abort, changing requests with route.continue, chaining with route.fallback, editing real responses with route.fetch, error, empty, slow and offline states, context-wide routes, and HAR files.
Network Interception and Mocking
- Modern pages load their data in the background. The page asks an API for products, orders or prices.
- A test can watch those calls, wait for them, and even answer them itself.
- That lets you test the empty list, the server error and the slow network, without breaking a real server.
Simple lesson: Playwright sits between the page and the server. It can watch, change or answer every call.
A Real-World Example
- The Practice Shop's Catalog page asks
/api/productsfor the list. - What does a shopper see when the shop has no products? The real server always has some.
- This test answers the call with an empty list and checks the message.
- The page never reached the server for products. Playwright answered with
[].
What You Will Learn
- How a page talks to the server, and how to watch it.
- Waiting for a call with
waitForResponse()andwaitForRequest(). - Intercepting calls with
page.route()and URL patterns. - Mocking, blocking, changing and chaining requests.
- Testing error, empty, slow and offline states.
- When to mock, and when to use the real backend.
How Pages Talk to Servers
Requests and Responses
- A request is the page asking for something: a method (
GET,POST), a URL, headers and maybe a body. - A response is the answer. It has a status code (200 OK, 404 Not Found, 500 Server Error), headers and a body, often JSON.
- The browser's
fetch()sends API requests. Playwright sees every one.
Animation: a request leaves the page, passes the route handler and reaches the server, and the response comes back. Then a second request is answered by route.fulfill without reaching the server
Page fetch()
page.route() your handler
Server /api/products
GET 200 [8] GET fulfill []
First call: passed to the server. Second call: answered by the handler.
A route handler stands between the page and the server. It can let a call pass, or answer it.
Watching Requests and Responses
page.on('request')runs for every request.page.on('response')runs for every answer.request.method(),request.url()andresponse.status()tell you what happened.
- The page also calls a weather service on another site. The lab has no internet, so that call fails and the page shows Weather unavailable.
Simple lesson: page.on('request') and page.on('response') show every call the page makes.
Waiting for Network Calls
waitForResponse()
- Sometimes you need the data the page received, not just what it shows.
page.waitForResponse(url)returns the response. Start waiting before the action that sends the call.
waitForRequest()
page.waitForRequest()returns the request. Use it to check what the page sent.- A function works as a filter:
request => request.url().includes('/api/analytics'). request.postDataJSON()reads a JSON body.
Start waitForResponse() or waitForRequest() before the click or goto() that sends the call. If you start after, the call may already be done and the wait times out.
Simple lesson: make the promise first, act second, await the promise third.
Intercepting with page.route()
URL Patterns
page.route(pattern, handler)catches every request whose URL matches.- A glob string:
**matches anything, including slashes.'**/api/products'matches the URLhttps://practice.devtutorials.ai/api/products. - A glob must match the whole URL.
'**/api/products'does not match/api/products?category=Shoes. Add*at the end:'**/api/products*'. - A regular expression (
/\/api\/products/) or a function (url => url.pathname === '/api/products') also works. - Register routes before
goto(). Calls made before the route exists are not caught.
Mocking a Response with route.fulfill()
route.fulfill({ json })answers with JSON and status 200.- Add
statusfor errors,bodyandcontentTypefor other content,headersfor custom headers.
- Mocking a third-party service, like the weather API, is very common. Your tests should not fail because someone else's server is down.
Blocking Requests with route.abort()
route.abort()makes the request fail, as if the network broke.- Use it to block analytics, ads and trackers in tests, or to test what happens when one call fails.
Changing Requests with route.continue()
route.continue()sends the request on to the server. You can change it on the way:url,method,headersorpostData.- Here, the page's orders call gets an
Authorizationheader, so the server treats it as Ben's.
Chaining Handlers with route.fallback()
- If several routes match, the one added last runs first.
route.fallback()passes the request to the next matching handler.route.continue()skips the others and goes to the server.{ times: 1 }makes a route work only once.page.unroute()removes it.
Simple lesson: fulfill answers, abort fails, continue sends on, fallback asks the next handler.
Modifying Real Responses
route.fetch() Then fulfill()
- Sometimes you want real data with one small change.
route.fetch()gets the real response. Change the JSON, thenroute.fulfill({ response, json }).- The status and headers stay real. Only the body changes.
Testing Hard-to-Reach States
Server Errors and Slow Responses
- Real servers rarely fail when you want them to. A mock fails on demand.
status: 500tests the error message. A delay beforeroute.continue()tests the loading state.
Going Offline
context.setOffline(true)cuts the network for the whole context. Every call fails.setOffline(false)turns it back on. The config optionoffline: truestarts offline.
In the lab, page.goto() still works while offline, because the practice pages are built in. A real offline browser cannot load new pages at all.
Routes for the Whole Context
context.route()works likepage.route(), but for every page in the context, including popups and new tabs.- Put it in a fixture or
beforeEachto block trackers in all tests.
Simple lesson: mocks make rare states easy: errors, empty lists, slow and offline.
Recording and Replaying (HAR)
What a HAR File Is
- A HAR file is a recording of real network traffic, saved as JSON.
page.routeFromHAR('example.har', { update: true })records the real answers once.- Later runs, without
update, answer every call from the file. No server needed. - The lab cannot record real traffic, so here it is plain code.
test('catalog from a recording', async ({ page }) => {
// First run: record. Then remove update: true to replay.
await page.routeFromHAR('hars/catalog.har', {
url: '**/api/**',
update: true,
});
await page.goto('/catalog');
await expect(page.getByRole('status').first()).toHaveText('8 products');
});
npx playwright open --save-har=hars/catalog.har https://practice.devtutorials.ai/catalogrecords while you click around.page.routeWebSocket()does the same kind of mocking for WebSockets, the live connections that chats use.
When to Mock
Mocks Versus the Real Backend
- Mock when the state is hard to make: errors, empty data, slow calls, third-party services.
- Use the real backend for the main happy paths. A test full of mocks can pass while the real app is broken.
- A good mix: most tests hit a real test server. A few focused tests mock the rare cases.
If the real API changes its JSON shape, your mocks will not notice. Keep mock data close to real data, and use API Testing to check the real API shape.
Simple lesson: mock the rare and the external. Test the main flows for real.
Try It in Your Project
- These steps take the lesson from the lab to your own app.
- Mock one third-party service your tests depend on, like payments or maps.
- Write one test each for a 500 error, an empty list and a slow response.
- Keep the main happy path on the real backend.
Simple lesson: use one idea from this lesson on your own app this week.
TL;DR
page.on('request' | 'response')watches calls.waitForResponse()andwaitForRequest()wait for one.page.route(pattern, handler)catches calls. Add it beforegoto().- Globs match the whole URL. Add
*for query strings. fulfillanswers,abortfails,continuesends on,fallbackpasses to the next handler.route.fetch()plusfulfill({ response, json })edits real data.setOffline(true)cuts the network.context.route()covers every page.
Common mistakes
| Mistake | What happens | Fix |
|---|---|---|
page.route() after page.goto() | The first calls are not caught | Add routes before goto() |
'**/api/products' for a URL with ?category= | The route never matches | Use '**/api/products*' or a regex |
Starting waitForResponse() after the click | The wait times out | Create the promise first, then click |
| A handler that never calls fulfill, abort, continue or fallback | The request hangs and the page waits forever | Always finish the route |
| Mocking every call in every test | Tests pass while the real app is broken | Mock rare and external cases only |
Check Yourself
Exercises
What to Remember
- Watch with
page.on('request' | 'response'). Wait withwaitForResponse()andwaitForRequest(). - Routes go in before
goto(). Globs match the whole URL. fulfill,abort,continue,fallback: answer, fail, send on, pass along.- Mocks make error, empty, slow and offline states easy.
- Mock the rare and external. Keep main flows real.
Next: API Testing