- Introduction
- Getting started
- Philosophy
- Comparison
- Limitations
- Debugging runbook
- FAQ
- Basics
- Concepts
- Network behavior
- Integrations
- API
- CLI
- Best practices
- Recipes
- Cookies
- Query parameters
- Response patching
- Polling
- Streaming
- Network errors
- File uploads
- Responding with binary
- Custom worker script location
- Global response delay
- GraphQL query batching
- Higher-order resolver
- Keeping mocks in sync
- Merging Service Workers
- Mock GraphQL schema
- Using CDN
- Using custom "homepage" property
- Using local HTTPS
FAQ
Common questions about Mock Service Worker.
Have a question not present in the list? Open a Discussion on GitHub and get help from our community.
How is it different than library XYZ?
Please see the Comparison page for detailed technical and conceptual comparison between Mock Service Worker and other popular API mocking libraries.
In a nutshell, most solutions provide requests interception on the application level, while Mock Service Worker intercepts requests on the network level. It also allows you to use the same mock definition not only for testing, but for development and debugging as well, integrating across different tools without configurations, adapters, or plugins.
Does it support request library XYZ?
Yes. Mock Service Worker supports all request libraries, both existing and those about to be released in the future. This is one of the benefits you get by mocking your API at the network level.
Can I use it in Node.js?
Yes. Although there’s no Service Worker in Node.js, MSW provides you with a designated API to reuse the same request handlers in Node.js. Follow the integration below to learn how to use MSW in Node.js:
Node.js integration
Learn how to integrate Mock Service Worker in any Node.js process.
Can I use it in React Native?
Yes, you can use MSW while developing and testing your React Native application. The setup would be similar to that in Node.js, and you can learn more about it following this guide:
Node.js integration
Learn how to integrate Mock Service Worker in a Node.js process.
ReferenceError: fetch
is not defined in Node.js
This error means that the version of Node.js you’re using doesn’t support the global Fetch API.
Resolve this by upgrading to Node.js version 18 or higher. MSW does not support Node.js versions below version 18.
Request
/Response
/TextEncoder
is not defined (Jest)
Make sure you are using Node.js v18 or newer before reading further.
This issue is caused by your environment not having the Node.js globals for one reason or another. This commonly happens in Jest because it intentionally robs you of Node.js globals and fails to re-add them in their entirely. As the result, you have to explicitly add them yourself.
Create a jest.polyfills.js
file next to your jest.config.js
with the following content:
// jest.polyfills.js
/**
* @note The block below contains polyfills for Node.js globals
* required for Jest to function when running JSDOM tests.
* These HAVE to be require's and HAVE to be in this exact
* order, since "undici" depends on the "TextEncoder" global API.
*
* Consider migrating to a more modern test runner if
* you don't want to deal with this.
*/
const { TextDecoder, TextEncoder } = require('node:util')
Object.defineProperties(globalThis, {
TextDecoder: { value: TextDecoder },
TextEncoder: { value: TextEncoder },
})
const { Blob, File } = require('node:buffer')
const { fetch, Headers, FormData, Request, Response } = require('undici')
Object.defineProperties(globalThis, {
fetch: { value: fetch, writable: true },
Blob: { value: Blob },
File: { value: File },
Headers: { value: Headers },
FormData: { value: FormData },
Request: { value: Request },
Response: { value: Response },
})
Make sure to install
undici
. It’s the official fetch implementation in Node.js.
Then, set the setupFiles
option in jest.config.js
to point to the newly created jest.polyfills.js
:
// jest.config.js
module.exports = {
setupFiles: ['./jest.polyfills.js'],
}
Pay attention it’s the setupFiles
option, and not setupFilesAfterEnv
.
The missing Node.js globals must be injected before the environment (e.g.
JSDOM).
If you find this setup cumbersome, consider migrating to a modern testing framework, like Vitest, which has none of the Node.js globals issues and provides native ESM support out of the box.
Why should I drop query parameters from the request handler URL?
Query parameters do not describe RESTful resources. Instead, they provide additional data to the server. Query parameters will be automatically stripped by MSW during the request matching and will have no effect.
It’s easier to understand this by thinking of request handlers as server-side route handlers: you do not include query parameters when routing on the server, so neither should you when routing with MSW.
Note that you can access query parameters in the request handler by using the URL
API:
// Describe the resource path: "/post".
http.get('/post', ({ request }) => {
// Convert the request URL string to a URL instance
// so the browser would parse query parameters for you.
const url = new URL(request.url)
// Access the query parameters from the URL instance.
// For example: GET /post/id=abc-123 → id: "abc-123"
const id = url.searchParams.get('id')
return HttpResponse.json({
id,
title: 'The Empowering Limitation',
})
})
Why do I get stale responses with react-query/SWR/Apollo/etc.?
Caching mechanism of some request clients may produce stale responses in your tests. Make sure you clear the cache before/after each test suite for your tests to remain predictable.
react-query
import { QueryCache } from 'react-query'
const queryCache = new QueryCache()
afterEach(() => {
queryCache.clear()
})
SWR
import { cache } from 'swr'
beforeEach(() => {
cache.clear()
})
Apollo Client
The Apollo Client team recommends creating a new client instance for each test.
// src/apollo-client.js
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'
const httpLink = new HttpLink({
uri: 'https://example.com/graphql',
})
export function makeClient() {
return new ApolloClient({
cache: new InMemoryCache(),
link: httpLink,
})
}
// test/Component.test.jsx
import { makeClient } from '../src/apollo-client'
it('renders the component', async () => {
const client = makeClient()
// ...use your client in test.
})
Learn more in the Apollo Client documentation.
Light theme when?
Whenever you have time to open a pull request.
MSW doesn’t work in concurrent tests
If your test suite features multiple concurrent test that modify the network behavior (i.e. have .use()
calls), you must scope the network to each test by using the server.boundary()
API.
server.boundary()
Scope the network interception to the given boundary.
You can only use server.boundary()
with setupServer()
. Browser tests do
not suffer from concurrency issues because the client runtime provides the
network isolation automatically.