Resource library

QA Interview

Playwright Network Mocking Interview Questions for Senior QA (2026)

Master Playwright network mocking interview questions senior QA engineers face, with precise answers on routing, HAR files, WebSockets, and test design.

22 min read | 4,391 words

TL;DR

Senior candidates should explain not only how to intercept a request, but why a chosen mock boundary preserves confidence. Strong answers distinguish fulfillment, mutation, fallback, aborts, HAR replay, service workers, WebSockets, and test-level isolation, then support the design with runnable Playwright code.

Key Takeaways

  • Use page.route() for page-scoped interception and context.route() when every page in a browser context needs the rule.
  • Choose route.fulfill(), route.continue(), route.fallback(), or route.abort() according to whether the test replaces, mutates, composes, or blocks traffic.
  • Register routes before navigation and match URLs narrowly to avoid silent over-mocking.
  • Treat HAR replay as a versioned contract fixture, not a permanent substitute for service integration tests.
  • Design mocks around observable behavior, schema-valid payloads, deterministic timing, and explicit cleanup.
  • Senior answers connect Playwright APIs to isolation, concurrency, security, and maintenance trade-offs.

Playwright network mocking interview questions senior QA engineers receive are rarely syntax quizzes. Interviewers want to see whether you can isolate a browser test without hiding integration risk, select the correct routing scope, model failures faithfully, and diagnose cases where interception never runs.

This guide gives 45 fully answered questions with TypeScript examples. Use it to rehearse concise explanations, then practice the code in a small Playwright project or in the QA interview practice workspace.

TL;DR

Topic Senior-level answer Primary API
Replace a response Fulfill the intercepted route with a realistic status, headers, and body route.fulfill()
Change an outgoing call Continue with deliberate URL, method, header, or body overrides route.continue()
Compose handlers Let another matching route inspect the request route.fallback()
Simulate transport failure Abort with a browser-supported error code route.abort()
Replay many calls Record and replay a reviewed HAR fixture page.routeFromHAR()
Observe without changing Subscribe to request and response events page.on()
Mock WebSockets Install a WebSocket route before the socket is created page.routeWebSocket()

The core distinction is simple: observation records what happened, while routing changes what happens. A senior engineer states the confidence lost through mocking and keeps a smaller set of real integration or contract checks to cover that gap. For broader preparation, pair this guide with Playwright interview questions and Playwright API testing interview questions.

1. Playwright Network Mocking Interview Questions Senior Candidates Get First

Q: 1. What is network mocking in Playwright?

Network mocking is the interception of browser-originated traffic so a test can block a request, alter it, synthesize a response, or replay recorded traffic. Playwright routes requests at the page or browser-context layer, before the application receives the response. The technique creates deterministic scenarios such as an empty catalog or a 503 response, but it does not prove the real backend still honors its contract. A senior test strategy therefore combines focused mocked UI tests with contract and end-to-end coverage.

Q: 2. How is network mocking different from API testing?

A mocked browser test validates how the frontend behaves against a controlled network outcome, whereas an API test sends real requests to a service and validates its deployed behavior. The mocked test is ideal for rare errors, precise boundary data, and fast UI feedback. The API test catches authentication, serialization, routing, persistence, and deployment defects that a fulfilled route bypasses. Neither is a replacement for the other because they answer different risk questions.

Q: 3. When should a senior QA engineer avoid mocking?

Avoid mocking the interaction that the test explicitly claims to verify, such as checkout integration with a payment sandbox or compatibility with a newly deployed API schema. Do not mock merely to silence an unstable environment without identifying the instability, because the suite can become green while production is broken. Prefer a real dependency for a thin critical-path layer, and use mocks lower in the pyramid for exhaustive UI states. Document each mock boundary so reviewers know which failures remain detectable.

Q: 4. What is the difference between page.route() and browserContext.route()?

page.route() affects requests initiated by one page, making it the safest default for a test-local scenario. browserContext.route() covers every page in that context, including popups, so it fits flows that cross windows or share a context fixture. Context routing has a wider blast radius and can surprise parallel tests if a context is reused. Both handlers should be installed before the request starts and removed when their lifetime is broader than one test.

Q: 5. Why must a route usually be registered before page.goto()?

Navigation immediately triggers the document request and often triggers scripts, configuration, and data calls. Registering afterward creates a race in which the target request may already have passed the interception point. Install deterministic prerequisites first, navigate second, then assert the user-visible result. If the request is caused by a later click, registration may happen after navigation but still must precede that click.

2. Core Playwright Routing APIs

Q: 6. When do you use route.fulfill()?

Use route.fulfill() when the test owns the response and should not contact the upstream server. Provide a status, content type, and serialized body that obey the consumer's schema. The following test is runnable against any project after replacing the example application URL with its local equivalent:

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

test('renders a mocked product', async ({ page }) => {
  await page.route('**/api/products', async route => {
    await route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify([{ id: 7, name: 'Network Lab' }]),
    });
  });

  await page.goto('https://example.test/products');
  await expect(page.getByText('Network Lab')).toBeVisible();
});

The assertion verifies business behavior rather than merely proving that the handler ran. In a real repository, also validate the fixture against the API schema.

Q: 7. What does route.continue() do?

route.continue() sends the request onward immediately, optionally overriding request properties. It is useful for adding a test header, changing a method, or replacing post data while preserving a live response. Overrides apply to the outgoing request, so changing postData without a matching content-type can produce an invalid server call. Because continue does not allow later matching handlers to run, use fallback when handler composition matters.

Q: 8. How does route.fallback() differ from route.continue()?

Both can modify a request and allow network activity, but route.fallback() passes control to the next matching handler before the request reaches the network. Playwright evaluates matching routes in reverse registration order, which lets a specific handler run before a broad default. This supports layered concerns such as authentication headers, endpoint-specific mutation, and final logging. route.continue() short-circuits that chain, so it should represent a deliberate final decision.

Q: 9. How do you simulate a network failure?

Call route.abort() to model a transport failure rather than an HTTP error response. For example, await route.abort('connectionrefused') makes the browser observe a refused connection, while fulfilling with status 500 means a server answered successfully at the transport layer. The application may handle those paths differently through retry, offline, or error-copy logic. Choose the failure code that corresponds to the requirement instead of using a generic abort everywhere.

Q: 10. How do you remove a route?

Use page.unroute(url, handler) when you need to remove one known handler, or page.unrouteAll() to clear all page routes. Keep the handler in a named variable if selective removal is required because a new inline function is not the same reference. Normally Playwright's fresh page and context fixtures isolate tests automatically. Explicit removal matters in reused fixtures, multi-phase tests, and helper libraries that temporarily install routing behavior.

3. URL Matching and Handler Precedence

Q: 11. Which URL matching forms does Playwright support?

A route can match with a glob string, a regular expression, or a predicate function receiving a URL object. Globs are readable for stable path patterns, regex handles constrained variations, and predicates are clearest for hostname, path, and query logic together. When a glob does not begin with *, Playwright resolves it against baseURL when configured. Senior engineers choose the narrowest readable matcher and add an assertion or counter when a false match would be dangerous.

Q: 12. Why can the glob **/api/* miss a request?

In Playwright glob syntax, a single * does not cross slash boundaries. The pattern can match one path segment after /api/, but it may miss /api/users/42/orders. Use **/api/** for arbitrary nested paths, or prefer a predicate when the host and pathname both matter. Also inspect the actual URL because version prefixes, trailing slashes, and query strings frequently explain the mismatch.

Q: 13. How do multiple matching route handlers execute?

The most recently registered matching handler runs first. If it calls fallback, Playwright continues to the next earlier handler; if it calls fulfill, abort, or continue, routing ends. This last-in-first-out behavior is useful but easy to obscure in helper-heavy suites. Keep broad handlers near fixture setup, register scenario-specific handlers later, and test the composition explicitly.

Q: 14. How would you match one query parameter safely?

Use a predicate and the URL API rather than a regex that accidentally depends on parameter order. For example, url => url.pathname === '/api/search' && url.searchParams.get('q') === 'playwright' expresses the actual condition. Add a hostname check when third-party traffic might share the same path. This avoids matching q=playwright-tools or failing when another parameter appears first.

Q: 15. How can you prove the expected request was intercepted?

Increment a test-local counter or resolve a promise inside the handler, then assert it after the user action. Do not rely only on a successful UI assertion because cached data or a different endpoint could produce the same screen. Keep the count exact when duplicate calls indicate a regression, and use expect.poll only when asynchronous settling truly requires it. Request verification should complement, not replace, the user-visible assertion.

4. Response Fulfillment and Mutation

Q: 16. How do you modify a real response instead of replacing it completely?

Fetch the upstream response through the intercepted route, parse its body, modify only the field needed by the scenario, and fulfill using that response as the base. This preserves status and most headers while making the intentional delta visible:

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

test('marks an upstream account as suspended', async ({ page }) => {
  await page.route('**/api/account', async route => {
    const response = await route.fetch();
    const account = await response.json();
    account.status = 'suspended';
    await route.fulfill({ response, json: account });
  });

  await page.goto('https://example.test/account');
  await expect(page.getByText('Account suspended')).toBeVisible();
});

This is a hybrid integration test because it still depends on the live endpoint. Avoid mutating shared objects, and verify that the upstream response is JSON before parsing it.

Q: 17. Why is the json option often better than manually setting body?

The json option serializes the supplied value and sets an appropriate JSON content type, reducing repetitive header and stringify code. A raw body remains useful for malformed JSON, text, XML, or byte-level scenarios. Whichever form you choose, model the production payload shape and encoding. A syntactically convenient mock with unrealistic nullability can teach the UI the wrong contract.

Q: 18. How do you preserve response headers while changing JSON?

Call route.fetch(), then pass the returned API response through the response property of route.fulfill() and override json. Playwright uses the original response as a basis while applying explicit overrides. Review cache, CORS, and content-length behavior if those headers are central to the scenario, since body replacement can make copied metadata misleading. For strict header testing, construct only the headers the application is expected to consume.

Q: 19. How do you mock a delayed response without making the suite flaky?

Delay inside the route handler with a small, controlled timer, then fulfill, but assert application state rather than elapsed wall-clock time. Use a delay just above the product's loading-state threshold when that threshold is contractual. Large sleeps slow every run and become vulnerable to loaded CI machines. For retry and timeout policies, prefer clock control where applicable and isolate the few tests that genuinely need network latency.

Q: 20. How do you model pagination correctly?

Read the request URL's page or cursor parameter and return a distinct schema-valid response for each value. Include termination semantics such as nextCursor: null, not just different item arrays, because the client uses those fields to stop fetching. Track calls to detect repeated cursor requests or duplicate page loads. A single static response cannot verify append behavior, deduplication, or end-of-list handling.

5. Request Mutation, Authentication, and Data

Q: 21. How do you add an authorization header to intercepted requests?

Clone the request headers and pass the merged object to route.continue() or route.fallback(). Never mutate the object returned by request.headers() in place and assume Playwright will apply it. Scope the route to the application API host so credentials are not forwarded to analytics or third-party origins. Prefer storage state or the application's normal login flow when authentication itself is under test.

Q: 22. How do you modify a POST body?

Parse route.request().postData() only when the content type is the format you expect, update a cloned value, and continue with serialized postData plus the correct header. For JSON, handle a missing body and parsing failure explicitly so the helper reports a useful test error. Mutation is valuable for boundary cases that the UI cannot produce, but an API test is usually a clearer home for extensive payload permutations. Avoid changing request data invisibly in a global fixture.

Q: 23. Can route.continue() redirect a request to another URL?

Yes, its url override can send the request to a different URL, but the replacement must keep the same protocol as the original. This is useful for routing a production-shaped frontend toward a local stub server. Make the override obvious in configuration and prevent accidental use outside tests. If CORS behavior is part of the requirement, URL rewriting may bypass precisely what you intended to validate.

Q: 24. How should secrets be handled in mocked tests?

Use fake tokens whose format is sufficient for client behavior, and never copy production credentials into fixtures, HAR files, traces, or test output. Redact authorization, cookies, and personal data before committing recorded network artifacts. If a local stub must validate a secret, inject a test-only value from CI secret storage. Include fixture scanning in review because HAR and trace archives can expose more than source code does.

Q: 25. How do you test token refresh behavior?

Model state in the handler: return 401 for the first protected request, return a valid refresh response, then allow or fulfill the retried protected request. Assert the refresh endpoint was called once and the original action completed without duplicate side effects. Keep counters inside the test so parallel workers do not share state. Also add a failure case where refresh returns 401 and the UI clears the session or redirects to login.

6. HAR Replay and Service Workers

Q: 26. What is HAR replay in Playwright?

HAR replay serves responses from a recorded HTTP Archive according to matching request data. page.routeFromHAR('fixtures/shop.har', { url: '**/api/**', notFound: 'abort' }) can reproduce a multi-request journey with less handler code. Treat the HAR as reviewed test data, because it captures headers, payloads, and timing metadata from a particular system state. Replay is deterministic, but it can become stale when contracts evolve.

Q: 27. What do notFound values mean in routeFromHAR()?

notFound: 'abort' fails unmatched traffic at the network layer, making an incomplete recording visible. notFound: 'fallback' lets unmatched requests continue through other routing or to the network. Abort gives stricter offline replay; fallback supports partial mocking. State which model the test uses, because silent fallback can turn a supposedly isolated suite into an environment-dependent one.

Q: 28. How do you update a HAR fixture?

Use the update option while intentionally recording against a controlled environment, then review the resulting file before committing it. Narrow the url filter so unrelated fonts, telemetry, and secrets do not enter the artifact. A refresh should be triggered by an understood contract change, not performed automatically whenever the test fails. Run schema checks or a semantic diff to reveal breaking payload changes hidden in a large HAR diff.

Q: 29. Why might page.route() not intercept a request handled by a service worker?

A service worker can satisfy a request before page routing sees it, so the expected handler never fires. For interception-focused tests, create the browser context with serviceWorkers: 'block'. If service-worker behavior is itself under test, do not block it; instead separate that suite and use observability appropriate to the worker flow. This distinction is a common explanation for a route that works in one environment but not another.

Q: 30. What are the maintenance risks of HAR files?

HAR artifacts are opaque compared with small typed fixtures, can contain sensitive data, and often change noisily. Their request matching may also encode transient query values or headers that reduce reuse. Keep recordings narrow, sanitize them, version them with the tests, and document the scenario represented. Prefer explicit route handlers when only one or two responses need control, because intent is easier to review.

For a deeper observation workflow before deciding what to mock, review capturing network traffic with Playwright.

7. WebSockets, Streaming, and Non-HTTP Cases

Q: 31. How does Playwright mock WebSockets?

Modern Playwright provides page.routeWebSocket() for WebSockets created after the route is installed. The handler can send messages to the page, inspect page messages, or connect to the real server and proxy selectively. Register it before navigation when the application opens a socket during startup. Keep message direction explicit because client-to-server and server-to-client frames often share similar JSON shapes.

Q: 32. Show a runnable WebSocket mock.

This example intercepts one socket, emits a server event, and responds to a client message without opening the real server:

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

test('shows a mocked WebSocket notification', async ({ page }) => {
  await page.routeWebSocket('**/ws', ws => {
    ws.onMessage(message => {
      if (message === 'subscribe:alerts') {
        ws.send(JSON.stringify({ type: 'alert', text: 'Build complete' }));
      }
    });
  });

  await page.goto('https://example.test/notifications');
  await expect(page.getByText('Build complete')).toBeVisible();
});

The application must send the expected subscription after connecting. A production test should also cover malformed frames, reconnection, and close behavior, while Playwright WebSocket frame inspection helps diagnose the live protocol first.

Q: 33. Can ordinary page.route() mock WebSocket frames?

No. HTTP routing may observe or affect the initial upgrade request in limited ways, but individual WebSocket frames require the WebSocket routing API. Treat connection establishment, frame protocol, reconnection, and UI projection as separate behaviors. Saying that all network traffic is handled by page.route() signals an incomplete mental model.

Q: 34. How would you test Server-Sent Events or streaming responses?

First determine whether the browser and Playwright routing layer can reproduce the exact streaming semantics your product requires. A single fulfilled body may validate final parsing but does not necessarily model incremental delivery, connection lifetime, or chunk boundaries. For those risks, use a tiny local HTTP server that emits controlled chunks or SSE events, then drive it through the browser. Keep a mocked final-state UI test as fast coverage, but retain an integration test for genuine streaming behavior.

Q: 35. How do you test WebSocket reconnection?

Use a controllable local socket server or WebSocket routing to close a connection, observe the client's retry attempt, and provide a successful subsequent connection. Assert capped backoff behavior through observable attempts without depending on fragile exact milliseconds unless timing is contractual. Verify subscriptions are restored once and messages are not duplicated after reconnect. A single happy connection says nothing about the most failure-prone part of a real-time client.

8. Debugging and Reliability

Q: 36. A route handler never runs. What do you inspect first?

Inspect the actual request URL and method using Playwright events or a trace, then compare it character by character with the matcher. Confirm the handler was registered before the triggering action, the request belongs to the expected page or context, and a service worker is not satisfying it. Next check whether a later-registered route handled it first without falling back. This order replaces guesswork with evidence and usually localizes the failure quickly.

Q: 37. Why does a mocked test pass alone but fail in parallel?

The usual causes are shared mutable counters, a reused browser context, a shared stub server port, or fixtures written to the same path. Keep handler state inside each test, let Playwright create isolated contexts, and allocate worker-specific external resources when necessary. Avoid process-global mock modes that one test can change while another runs. Reproduce with multiple workers and repeat runs because the defect is an isolation failure, not merely bad luck.

Q: 38. How do you observe traffic without intercepting it?

Subscribe to page.on('request'), page.on('response'), and page.on('requestfailed') to collect diagnostics without changing traffic. page.waitForResponse() can synchronize on a specific response when a user action triggers it, but the UI assertion should still establish business success. Event listeners should filter aggressively to keep logs useful and avoid leaking credentials. Observation is preferable when the test needs evidence but should preserve real backend behavior.

Q: 39. Why is waitForTimeout() a poor network synchronization strategy?

A fixed sleep guesses when a request will finish, wasting time on fast runs and failing on slow ones. Wait for a response predicate, a stable UI state, or an application-specific event instead. If the objective is to display a loading indicator, control the route and assert the indicator before resolving the response. Time should represent a product requirement, not compensate for missing synchronization.

Q: 40. How do traces help debug network mocks?

A Playwright trace correlates actions, DOM snapshots, console output, and network activity around the failure. It can reveal that the page called a different endpoint, sent unexpected data, or received a response before the route was active. Retain traces on first retry in CI to balance evidence and storage. Combine trace review with handler-side assertions because a trace explains execution, while assertions encode the intended contract.

The Playwright debugging questions for senior QA guide expands this evidence-first approach.

9. Architecture and Test Strategy

Q: 41. Where should reusable mock handlers live?

Place domain-specific builders near test fixtures, with typed inputs and conservative defaults, rather than one enormous global routing file. A builder such as buildAccount({ status: 'suspended' }) communicates the scenario while a route helper owns transport details. Keep the final assertion in the test so the expected user behavior remains visible. Version fixture schemas with the application contract and delete variants that no test uses.

Q: 42. How do you prevent mocks from drifting from production contracts?

Generate types from the service schema where practical, validate fixtures at runtime in a focused check, and run provider or consumer contract tests in CI. Review production-compatible examples with API owners when optionality or enum values change. A mock should fail loudly when required fields disappear instead of allowing structural typing shortcuts to hide the change. Maintain a smaller real-backend suite as the final signal that client and service still integrate.

Q: 43. What should be mocked in a microservices UI flow?

Mock at the boundary that isolates the behavior under examination, usually the browser's backend-for-frontend calls rather than every downstream microservice. Mocking internal services from a browser test couples UI automation to architecture it does not own. Use service-level component and contract tests for downstream interactions. For one critical journey, exercise the deployed chain so routing, authentication, and data propagation remain covered.

Q: 44. How do you organize positive and negative mock scenarios?

Name scenarios by business outcome, such as expired subscription blocks export, instead of transport mechanics such as returns 403. Build the smallest response needed for that outcome and assert accessible UI behavior, retry rules, and side effects. Parameterize only scenarios that share both setup and meaning; otherwise separate tests preserve diagnosis. Negative cases should distinguish transport errors, server errors, validation errors, empty states, and malformed payloads because clients often handle them differently.

Q: 45. How would you review a pull request full of network mocks?

Check that matchers are narrow, handlers are registered before triggers, payloads satisfy current schemas, and assertions prove user behavior plus meaningful request details. Look for credentials in fixtures, broad context routes, silent live-network fallback, arbitrary sleeps, and state shared across workers. Ask which integration risk each mock removes and where that risk is covered elsewhere. Finally, run the tests with parallel workers and inspect at least one intentional failure to judge diagnostic quality.

10. Playwright Network Mocking Interview Questions Senior Design Exercise

A common design prompt is: test an orders page that loads a list, retries one transient failure, refreshes an expired token, and receives shipping updates over WebSocket. Start by splitting observable behaviors. Use request routing for the list and token refresh, stateful counters scoped to one test for the single retry, and WebSocket routing for the shipping event. Keep the test focused enough that a failure identifies one policy rather than a whole orchestration.

A strong answer also identifies what stays real. Contract tests should verify order and refresh payloads against the services, while one deployed journey verifies gateway, identity, and socket infrastructure. The browser mocks then cover error copy, spinners, retry buttons, duplicate suppression, and accessibility states cheaply. This layered proposal demonstrates architecture judgment, not just API recall.

Use this compact setup for HTTP error behavior:

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

test('recovers from one orders failure', async ({ page }) => {
  let attempts = 0;
  await page.route('**/api/orders', async route => {
    attempts += 1;
    if (attempts === 1) {
      await route.fulfill({ status: 503, json: { error: 'temporarily_unavailable' } });
      return;
    }
    await route.fulfill({ status: 200, json: { orders: [{ id: 'O-42' }] } });
  });

  await page.goto('https://example.test/orders');
  await page.getByRole('button', { name: 'Retry' }).click();
  await expect(page.getByText('O-42')).toBeVisible();
  expect(attempts).toBe(2);
});

In an interview, explain that example.test represents the candidate's app and that the selectors depend on its UI. The code uses current Playwright APIs and contains no invented helper.

11. How Interviewers Grade Your Answers

Interviewers usually grade on four dimensions. First is API correctness: you must distinguish fulfill, continue, fallback, fetch, abort, HAR replay, events, and WebSocket routing. Second is test design: the mocked outcome should map to a business behavior with assertions at the UI and request boundaries. Third is operational judgment: isolation, parallelism, secret handling, fixture drift, tracing, and cleanup separate senior answers from basic snippets. Fourth is risk awareness: state explicitly what the mock cannot prove and name the complementary contract or integration check.

For coding exercises, narrate the order of operations: install the route, trigger the request, assert the result, and verify interception count when duplication matters. Prefer typed, local data and narrow matchers. If requirements are ambiguous, ask whether the goal is frontend state coverage, transport behavior, or real service integration before choosing the mechanism. You can also upload your resume to the QAJobFit dashboard to align your project evidence with senior automation roles.

12. Common Mistakes

  • Registering a route after navigation has already started the target request.
  • Matching **/* and unintentionally mocking documents, assets, analytics, or third-party calls.
  • Returning a happy-path body that does not match the production schema.
  • Calling continue() when another route was expected to receive control through fallback().
  • Treating status 500 and an aborted connection as equivalent failures.
  • Sharing counters or mock modes between parallel tests.
  • Leaving HAR files unsanitized or allowing unmatched calls to reach live systems silently.
  • Asserting only that a route ran, without checking the application's user-visible behavior.
  • Using fixed sleeps to wait for network completion.
  • Mocking the dependency that the test claims to integrate with.

The remedy is a small, explicit mock boundary backed by schema checks and real integration coverage. Before merging, force the mock to fail once and confirm the report explains the broken expectation clearly.

Conclusion

The best response to Playwright network mocking interview questions senior interviewers ask combines exact routing semantics with test-strategy judgment. Know how to implement HTTP fulfillment, request mutation, handler composition, HAR replay, service-worker controls, and WebSocket routing, but always connect the code to a business risk and an observable assertion.

Practice by building three cases: a schema-valid success, a transport abort, and a stateful retry. Then explain which defects those tests can catch, which defects they cannot catch, and where the remaining integration confidence comes from.

Interview Questions and Answers

What is the difference between route.continue() and route.fallback()?

Both can override an outgoing request. continue sends it onward immediately and prevents other matching routes from running, while fallback passes control to the next matching handler before the network. I use fallback for composable routing layers and continue when the handler makes the final routing decision.

How would you mock a 500 response in Playwright?

I register a narrow route before the triggering action and call route.fulfill() with status 500, JSON content, and a schema-appropriate error body. Then I assert the exact user-facing recovery behavior and verify the endpoint was called as expected. I use route.abort() instead when the requirement is a transport failure.

When is route.fetch() useful?

route.fetch() is useful when I want the real upstream response but need to inspect or modify it before the page receives it. I can pass that response into route.fulfill() and override selected JSON data. The test remains dependent on the upstream system, so I do not describe it as fully isolated.

How do you test retry behavior without flaky sleeps?

I keep a counter inside the test route, return the intended failure on the first call, and return success on the next call. I wait on observable UI states or response events rather than a fixed timeout. Finally, I assert the exact request count to detect duplicate retries.

How do you handle service workers during network interception tests?

A service worker may handle a request before page routing sees it. For tests focused on Playwright interception, I create the context with serviceWorkers set to block. I keep separate tests with workers enabled when offline caching or worker behavior is the subject.

What is your strategy for HAR replay?

I use HAR replay for multi-request flows where a reviewed recording provides value over many explicit handlers. I restrict the URL scope, sanitize secrets, choose abort for strict unmatched-request detection, and review semantic changes when updating. Contract tests and a thin live journey cover drift risk.

How do multiple Playwright route handlers interact?

Matching handlers are considered in reverse registration order. A handler can call fallback to reach the next one, while fulfill, abort, or continue ends routing. I register broad fixture behavior first and scenario-specific behavior later, keeping the order visible.

How would you mock an authenticated request safely?

I scope the route to the intended API origin, clone headers, and add a fake test token through continue or fallback. I never put production credentials in source, HAR, traces, or logs. If login is under test, I preserve the application's real authentication path instead of injecting a header.

Can page.route() mock WebSocket frames?

No, ordinary HTTP routing is not the API for individual frames. I use page.routeWebSocket() before the socket is created, then inspect and send messages or proxy to the server. I separately test reconnection, duplicate subscriptions, and malformed messages.

How do you decide what not to mock?

I leave real the boundary whose integration the test claims to prove. Mocked UI tests cover deterministic states and rare failures, while contract tests and a small deployed end-to-end layer cover schemas, authentication, routing, and infrastructure. I document this division so a green suite has an honest meaning.

Frequently Asked Questions

What Playwright APIs are most important for network mocking interviews?

Know page.route(), browserContext.route(), route.fulfill(), route.continue(), route.fallback(), route.abort(), route.fetch(), page.routeFromHAR(), and page.routeWebSocket(). Also explain request and response events because observation is different from interception.

Does Playwright network mocking call the real backend?

A route fulfilled directly does not call the backend. A continued request, a fallback that reaches the network, or route.fetch() does call an upstream service, so the test is only partially mocked.

Should I use page.route() or browserContext.route()?

Use page.route() for a rule local to one page. Use browserContext.route() when popups or multiple pages in the same isolated context must share interception, and clean up broad routes carefully.

Can Playwright mock WebSocket messages?

Yes. page.routeWebSocket() can intercept newly created sockets, send messages to the page, inspect page messages, or proxy to a real server. Register the route before the application opens the socket.

Why does my Playwright route not intercept a request?

Common causes are late registration, an incorrect glob, a different page or context, a later route that handled the request first, or a service worker. Log the actual URL and inspect a trace before widening the matcher.

Is HAR replay better than explicit route handlers?

HAR replay is efficient for a journey with many stable calls, while explicit handlers make small scenarios easier to understand and review. HAR files need sanitization, deliberate updates, and controls for unmatched requests.

How do senior QA engineers prevent mock drift?

They derive types from schemas where possible, validate fixtures, run contract tests, and retain a thin real-backend suite. They also make fixture changes part of normal code review rather than silently rerecording data.

Related Guides