React

Mocking the API with MSW: a faked network, real code

MSW intercepts network requests and answers with handlers: the component runs its real code and the test needs no external server.

Key points

  • MSW intercepts the network: the component uses real fetch or axios.
  • onUnhandledRequest: error fails any request you did not plan.
  • The handlers stay out of the production bundle.

MSW intercepts requests at the network layer and answers with handlers: the component runs its real code, and only the server changes.

Why not mock fetch

Replacing fetch with a fake function ties the test to the implementation: when the component moves to axios, the test breaks. MSW intercepts the request and returns a complete HTTP response.

An handler

import { http, HttpResponse } from "msw";

export const handlers = [
  http.post("/api/login", async ({ request }) => {
    const { email } = await request.json();
    if (!email) return HttpResponse.json({ error: "email required" }, { status: 422 });
    return HttpResponse.json({ token: "test-token" });
  }),
];

The handler answers like the server: status code, JSON body and a delay when needed. The test checks how the component behaves against a real response, not a fake promise.

Wiring it into tests

import { setupServer } from "msw/node";
import { handlers } from "./handlers";

const server = setupServer(...handlers);

beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

onUnhandledRequest: "error" is the part that matters: a request you did not plan fails the test instead of reaching the network.

Installation

npm i -D msw
npx msw init public/ --save

MSW ships under the MIT licence. The msw init command copies the service worker into public and records the path in package.json.

Live records in the catalogue

Three components that call an API: login-form, multistep-form and file-upload-card. To browse by category start from 111,985 components.

The limitation

MSW imitates the network, not the browser: it checks neither CORS, nor third-party cookies, nor proxy behaviour. A permissive handler also hides the very case that breaks the page.

Where to continue

The test picture is in Testing Library: five tests; data fetching without libraries is in data fetching without libraries; the comparison between Server Actions and an API is in Server Actions vs API.

Frequently asked questions

Does MSW replace the server in end-to-end tests?

No. It does for component and integration tests; a real browser needs a test server or Playwright routes.

Does it work with axios and fetch?

Yes. It intercepts the HTTP request, so the library in use does not matter.

How do I simulate a network error?

With HttpResponse.error() in the handler: the client gets a network error, not a response.

Do handlers ship in production code?

No. Keep them in a test folder or in src/mocks, outside the bundle.

Can I use MSW in the browser while developing?

Yes, with the service worker started from the generated file: the same handlers as the tests.