Concept
What is Mock Service Worker (MSW)?
Traditional network mocking involves stubbing client libraries (like axios or fetch variables). This couples tests to specific libraries and does not verify actual request layouts.
Mock Service Worker (MSW) intercepts network calls at the browser's Service Worker level (or intercepts raw HTTP streams in Node.js processes). The application makes real network requests over the wire, and MSW intercepts them at the boundary, returning mock responses:
Client (fetch) ──▶ Service Worker (MSW interceptor) ──▶ Mock JSON response
(does NOT hit live API servers)Implementing MSW Handlers
We declare request handlers matching HTTP routes, returning status codes and mock JSON payloads:
// src/mocks/handlers.js
import { http, HttpResponse } from 'msw';
export const handlers = [
http.get('https://api.example.com/users', () => {
return HttpResponse.json([
{ id: 1, name: 'Ada Lovelace' }
]);
}),
http.post('https://api.example.com/users', async ({ request }) => {
const newUser = await request.json();
return HttpResponse.json(newUser, { status: 201 });
})
];Setting up the Server Interceptor for Node/Testing
To run MSW inside unit/integration test runner processes (which run in Node/jsdom, where service workers are unavailable), MSW uses HTTP request interceptors:
// src/mocks/server.js
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
// tests/setup.js
import { beforeAll, afterEach, afterAll } from 'vitest';
import { server } from '../src/mocks/server';
beforeAll(() => server.listen()); // Start intercepting
afterEach(() => server.resetHandlers()); // Reset handler adjustments
afterAll(() => server.close()); // Terminate interceptorCommon Mistakes
1. Mocking individual libraries (like axios) alongside MSW
If you write mock imports (e.g. vi.mock('axios')) inside your test files while running MSW, you override the HTTP client, rendering MSW useless. Rely exclusively on MSW's network-level interception.
2. Forgetting to call server.resetHandlers()
If you override an MSW handler dynamically for a single test case (using server.use()) and do not reset handlers in afterEach(), the override leaks to subsequent tests, causing unexpected assertions.
Best Practices
- Match Production Base URLs: Ensure your MSW handler URLs match your production API endpoints exactly, ensuring tests reflect real network paths.
- Run MSW in Dev and Testing: Share MSW handlers between local browser development (using service workers) and automated integration tests (using node server interception).
- Test Server Errors: Write error scenarios (like returning
HttpResponse.json(null, { status: 500 })) to verify that the application handles API failures gracefully.
