Network Request Mocking with MSW
Network request mocking intercepts HTTP requests so frontend tests can exercise realistic success, loading, and failure states without contacting real backend services. Mock Service Worker (MSW) defines request handlers at the network boundary, keeping application code unaware that a response is mocked.
Network mocking replaces the real server response with a controlled response during a test. Instead of mocking a component's `fetch` call or a particular HTTP client method, tools such as MSW intercept requests at the network boundary and return data defined by the test environment.
A useful test setup defines handlers for endpoints such as `GET /api/users` or `POST /api/login`. Individual tests can override a handler to represent conditions such as an HTTP error, delayed response, empty data, or malformed payload. This lets the same React component be tested against multiple realistic server behaviors.
MSW can operate differently depending on the environment. In browser-based development or browser tests it can use a service worker, while Node-based tests can use request interception mechanisms provided by MSW. The application generally continues making ordinary HTTP requests, which reduces coupling between production code and the mocking setup.
Good network mocks model the API contract rather than internal implementation details. For example, a test should usually assert what the UI displays after a `GET /api/profile` request fails, rather than asserting that a particular mocking function was called. This makes tests more resilient when the underlying HTTP client or component implementation changes.
Network mocking is especially valuable for asynchronous UI behavior: initial loading, successful data rendering, retries, authentication failures, validation errors, and server outages. It should complement unit tests rather than replace tests of pure functions or the real backend/API integration.
Key points
- Mock requests at the network boundary instead of mocking component internals.
- Define reusable handlers for the API behavior that the application expects.
- Override handlers per test to exercise success, error, empty, and delayed responses.
- Keep assertions focused on observable UI behavior and API outcomes.
- Use real backend integration tests separately when validating the actual service contract.
Interview questions
Why would you use MSW instead of mocking `fetch` directly?
MSW lets the application continue making its normal HTTP request while the test controls what the server returns. That makes the test less coupled to the particular HTTP client or implementation used by the component. It also makes request behavior resemble a real client-server interaction, which is useful for testing asynchronous UI states. Directly mocking `fetch` can still be appropriate for narrowly scoped unit tests, but it operates at a lower implementation level.
How would you test a React component that displays loading, success, and error states?
I would define a normal handler for the successful response and render the component under a test server setup. The test would first assert the loading state, then wait for the successful data to appear. A separate test would override the endpoint handler to return an error status and assert the error UI. If the component supports retries, I would also verify that a subsequent request can receive a successful response.
Where should network handlers live in a frontend test suite?
Reusable handlers generally belong in test-support code that can be shared across tests. A test setup can register common handlers and start the mock server or interception mechanism before tests run. Individual tests can temporarily override a handler when they need unusual behavior. The setup should also restore handlers and shut down the mock infrastructure after the test suite finishes.
What makes a network mock trustworthy?
A trustworthy mock should reflect the API contract closely enough that tests exercise realistic application behavior. Response shapes, relevant status codes, and important error conditions should match what the application can actually receive. Mocks should not become an alternative specification that silently diverges from the backend. I would pair them with contract or integration testing where appropriate so that the mocked assumptions are periodically checked against the real service.
How would you test authentication failures or authorization errors with network mocking?
I would keep the normal request path intact and configure the relevant endpoint to return the status and response body expected from the service, such as `401` for an unauthenticated request or `403` for an authenticated user without permission. The test would then verify the application's observable response, such as redirecting to login or showing an access-denied message. I would avoid asserting that a particular internal function was invoked unless that behavior itself is the subject of the test. Separate tests can cover each meaningful server response.