# Mocking the Network with MSW — Jest, Vitest & Testing Library

Source: https://www.skillbyai.com/en/javascript-testing/m-msw

> Intercept requests instead of replacing fetch.

## Mock at the network boundary

Mocking `fetch` or `axios` directly ties tests to one HTTP client and to exact call shapes. **Mock Service Worker (MSW)** intercepts requests at the network level, so your real fetch code, headers, URL building and JSON parsing all run. You describe **request handlers** (`http.get('/api/user/:id', resolver)`) that return `HttpResponse.json(...)`. In Node test runners, `setupServer` from `msw/node` installs them; in the browser, the same handlers can run in a service worker for development. A common pattern: default happy-path handlers in a shared file, `server.use(...)` inside a test to override one endpoint with an error or empty state, and `server.resetHandlers()` after each test. The examples use the MSW 2.x API; earlier versions used `rest` and `res(ctx...)`, so check the docs for your version.

## MSW server in a setup file and a per-test override

MSW 2.x syntax.

```typescript
// src/test/handlers.ts
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/api/users/:id', ({ params }) =>
    HttpResponse.json({ id: params.id, name: 'Asha' }),
  ),
];

// src/test/server.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);

// src/test/setup.ts (listed in setupFiles)
import { afterAll, afterEach, beforeAll } from 'vitest';
import { server } from './server';
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

// users.test.ts
import { http, HttpResponse } from 'msw';
import { server } from './test/server';
import { fetchUser } from './users';

it('surfaces a server error', async () => {
  server.use(http.get('/api/users/:id', () => new HttpResponse(null, { status: 500 })));
  await expect(fetchUser('42')).rejects.toThrow(/500/);
});
```

## Fail on unhandled requests

`onUnhandledRequest: 'error'` makes any request without a handler fail the test, so a typo in a URL or a forgotten endpoint is caught immediately instead of silently hitting nothing. Relative URLs in Node may need a base URL in your client config.

**Quiz:** What is the main advantage of MSW over mocking fetch directly?

- [ ] It makes real calls to production
- [x] Your real request code runs and tests do not depend on which HTTP client you use
- [ ] It removes the need for async tests
- [ ] It only works with GraphQL

*Answer:* Your real request code runs and tests do not depend on which HTTP client you use. Mock the network, not the client.
