Vitest Cheatsheet
Network Mocking (MSW)
Use this Vitest reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
What is MSW?
Mock Service Worker intercepts HTTP requests at the network layer — your code runs as normal, but responses are intercepted by your handlers. Works in both browser (via Service Worker) and Node (via interceptors).
Installation
npm install -D msw
Defining Request Handlers
// src/mocks/handlers.ts import { http, HttpResponse } from 'msw' export const handlers = [ // GET with path params http.get('/api/users/:id', ({ params }) => { const { id } = params return HttpResponse.json({ id, name: 'Alice' }) }), // POST with request body http.post('/api/users', async ({ request }) => { const body = await request.json() return HttpResponse.json({ id: '123', ...body }, { status: 201 }) }), // Simulate error http.get('/api/broken', () => { return HttpResponse.json({ message: 'Internal Server Error' }, { status: 500 }) }), // Network error (no response) http.get('/api/timeout', () => { return HttpResponse.error() }), ]
Setting Up the Node Server (for Vitest)
// src/mocks/server.ts import { setupServer } from 'msw/node' import { handlers } from './handlers' export const server = setupServer(...handlers)
Global Setup in setupFiles
// src/test/setup.ts import { beforeAll, afterEach, afterAll } from 'vitest' import { server } from '../mocks/server' beforeAll(() => server.listen({ onUnhandledRequest: 'error' })) afterEach(() => server.resetHandlers()) afterAll(() => server.close())
// vitest.config.ts export default defineConfig({ test: { setupFiles: ['./src/test/setup.ts'], }, })
Using MSW in Tests
import { describe, it, expect } from 'vitest' import { fetchUser } from './api' it('fetches a user', async () => { const user = await fetchUser(1) expect(user.name).toBe('Alice') // MSW handler returns this })
Overriding Handlers Per Test
import { server } from '../mocks/server' import { http, HttpResponse } from 'msw' it('handles 404', async () => { server.use( http.get('/api/users/:id', () => HttpResponse.json({ message: 'Not found' }, { status: 404 }) ) ) await expect(fetchUser(999)).rejects.toThrow('Not found') }) // handler is reset after each test because of server.resetHandlers() in setup
One-Time Handler Override
server.use( http.get('/api/data', () => HttpResponse.json({ val: 42 }), { once: true }) ) await fetchData() // returns { val: 42 } await fetchData() // falls through to original handler
HTTP Methods
import { http, HttpResponse } from 'msw' http.get('/path', resolver) http.post('/path', resolver) http.put('/path', resolver) http.patch('/path', resolver) http.delete('/path', resolver) http.head('/path', resolver) http.options('/path', resolver)
Reading Request Data
http.post('/api/login', async ({ request, params, cookies }) => { const body = await request.json() // JSON body const text = await request.text() // text body const form = await request.formData() // FormData const url = new URL(request.url) const q = url.searchParams.get('query') // query params const auth = request.headers.get('Authorization') return HttpResponse.json({ token: 'abc' }) })
Returning Different Response Types
// JSON HttpResponse.json({ key: 'value' }, { status: 200 }) // Text HttpResponse.text('plain text', { status: 200 }) // XML new HttpResponse('<root/>', { headers: { 'Content-Type': 'application/xml' } }) // Blob/ArrayBuffer new HttpResponse(new Blob(['binary']), { headers: { 'Content-Type': 'application/octet-stream' } }) // Empty (204) new HttpResponse(null, { status: 204 }) // Network failure (no response) HttpResponse.error()
Passthrough (Let Real Request Through)
import { passthrough } from 'msw' http.get('/api/real-endpoint', () => passthrough())
GraphQL Handlers
import { graphql, HttpResponse } from 'msw' export const handlers = [ graphql.query('GetUser', ({ variables }) => { return HttpResponse.json({ data: { user: { id: variables.id, name: 'Alice' } }, }) }), graphql.mutation('CreateUser', ({ variables }) => { return HttpResponse.json({ data: { createUser: { id: '1', name: variables.name } }, }) }), ]
Browser Setup (for Storybook / Component Tests)
// src/mocks/browser.ts import { setupWorker } from 'msw/browser' import { handlers } from './handlers' export const worker = setupWorker(...handlers)
// main.tsx (dev only) if (import.meta.env.DEV) { const { worker } = await import('./mocks/browser') await worker.start() }
Generate the Service Worker:
npx msw init public/ --save
Unhandled Request Strategies
| Strategy | Effect |
|---|---|
'bypass' | Let the request through (default) |
'warn' | Console warning for unhandled |
'error' | Throw an error (recommended for tests) |
server.listen({ onUnhandledRequest: 'error' })
Common Gotchas
- Forget
afterEach(() => server.resetHandlers())— per-test overrides bleed into other tests. HttpResponse.error()vs 500 —.error()simulates a network failure (no response); a 500 status is a valid HTTP response.- MSW v1 vs v2 API — v2 uses
HttpResponse(notres(ctx.json(...))). This cheatsheet covers v2. onUnhandledRequest: 'error'— catches missing handlers early; remove for gradual adoption.