- 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
Comparison
Mock Service Worker comparison with similar tools.
Choosing the right tool for the job is crucial. We tried our best to provide a comprehensive and unbiased comparison between Mock Service Worker and other open-source API mocking libraries below.
The purpose of this page is not to reason about what technology is better or worse. All the libraries listed here have a place to be, and grading them is not only subjective, but also disrespectful towards the people who really should be using one over another.
Comparison criteria
Any good comparison begins with a clearly defined set of criteria. Here are the criteria we used when comparing MSW to alternatives (written from a developer’s perspective):
- Supported API types. What kinds of API can I mock with the library?
- Environment. In which environments can I use the library?
- Implementation. How does the library implement request interception?
- Integration. How much effort it takes to integrate it into my project?
- Definition. How do I define mocks?
Nock
Nock is an HTTP server mocking and expectations library for Node.js.
Nock is a great library for combining API mocking and assertions in Node.js. While Mock Service Worker doesn’t come with a built-in assertion capabilities, it allows you to seamlessly reuse the same mocks across browser and Node.js, as well as providing first-class GraphQL support and standard-based request/response handling.
API support
API type | Nock | Mock Service Worker |
---|---|---|
REST API | ✅ | ✅ |
GraphQL API | ❌ | ✅ |
WebSocket API | ❌ | ❌ |
Supported environment
Environment | Nock | Mock Service Worker |
---|---|---|
Node.js | ✅ | ✅ |
Browser | ❌ | ✅ |
Integration
Nock | Mock Service Worker |
---|---|
Does not require any changes to the code. | Does not require any changes to the code. |
Requires additional adapters to intercept specific request clients (e.g. axios ) | Works with any request client without additional configuration. |
Definition
Nock
Nock uses method chaining to intercept requests and declare mocked responses:
nock('https://api.example.com').get('/user').reply(200, { id: 1, name: 'John' })
Mock Service Worker
MSW models its interception API after server-side routing and handles requests and responses according to the Fetch API specification, using the same classes you would use normally in JavaScript:
http.get('https://api.example.com/user', async ({ request }) => {
const payload = await request.json()
return HttpResponse.json({ id: 1, name: 'John' })
})
JSON Server
JSON Server allows you to create an actual HTTP server based on a JSON file.
JSON Server is an actual HTTP server that utilizes abstract response definition format. This means a server you have to run and maintain. Mock Service Worker doesn’t spawn any servers, so its initialization cost is free. Mock Service Worker also allows you to have a more error-proof mock definitions by using languages like TypeScript and generating mocks out of the actual backend implementation.
API support
API type | JSON Server | Mock Service Worker |
---|---|---|
REST API | ✅ | ✅ |
GraphQL API | ❌ | ✅ |
WebSocket API | ❌ | ❌ |
Supported environment
Environment | JSON Server | Mock Service Worker |
---|---|---|
Node.js | ✅ | ✅ |
Browser | ✅1 | ✅ |
1—JSON Server doesn’t actually run in the browser. Since it’s a standalone server, it can be requested from anywhere in your system.
Integration
JSON Server | Mock Service Worker |
---|---|
Requires to change the code to request resources from the mocked server. | Does not require any changes to the code. |
Works with any request client without additional configuration. | Works with any request client without additional configuration. |
Definition
JSON Server
JSON Server uses a resource-first routes format that implicitly generates request paths and describes mocked responses.
{
"posts": [
{ "id": 1, "title": "json-server" },
{ "id": 2, "title": "mock-service-worker" }
]
}
This will automatically generated server-side routes like
GET /posts
andDELETE /posts/:index
based on the described resources. The nature of such routes is static and requires additional abstractions to achieve more complex network behaviors.
Mock Service Worker
MSW models its interception API after server-side routing and handles requests and responses according to the Fetch API specification, using the same classes you would use normally in JavaScript:
http.get('/posts', () => {
return HttpResponse.json([
{ id: 1, title: 'json-server' },
{ id: 2, title: 'mock-service-worker' },
])
})
With MSW, you have to explicitly define server-side operations like
http.get('/posts')
orhttp.delete('/posts/:index')
. MSW prioritized explicitness, taking advantage of programmatic request resolution, allowing for more complex network behaviors.
Mirage
Mirage is an API mocking library that lets you build, test and share a complete working JavaScript application without having to rely on any backend services.
Mirage is centered around data modeling to emulate more complex server behaviors. Mock Service Worker doesn’t come with a built-in data modeling capabilities and instead exposes them in a separate package called
@mswjs/data
. This way you can bring modeling to the table once you need it.
API support
API type | Mirage | Mock Service Worker |
---|---|---|
REST API | ✅ | ✅ |
GraphQL API | ❌ | ✅ |
WebSocket API | ❌ | ❌ |
Supported environments
Environment | Mirage | Mock Service Worker |
---|---|---|
Node.js | ❌ | ✅ |
Browser | ✅ | ✅ |
Implementation
Environment | Mirage | Mock Service Worker |
---|---|---|
Browser | Monkey-patches fetch and XMLHttpRequest (uses pretender ). | Uses a Service Worker to intercept requests on the browser level. |
Integration
Mirage | Mock Service Worker |
---|---|
Does not require any changes to the code. | Does not require any changes to the code. |
Works with any request client without additional configuration. | Works with any request client without additional configuration. |
Definition
Mirage
Mirage uses route handlers format to define server-like routes and mocked responses.
createServer({
routes() {
this.get('/movies', () => {
return ['Interstellar', 'Inception', 'Dunkirk']
})
},
})
Mirage also comes with an in-memory database to help you model more complex data relationships.
createServer({
models: {
movie: Model.extend({
castMembers: hasMany(),
}),
castMember: Model.extend({
movie: belongsTo(),
}),
},
routes() {
// Your route handlers responding with the data.
},
})
Mock Service Worker
MSW models its interception API after server-side routing and handles requests and responses according to the Fetch API specification, using the same classes you would use normally in JavaScript:
http.get('/movies', () => {
return HttpResponse.json(['Interstellar', 'Inception', 'Dunkirk'])
})
Although MSW doesn’t ship data modeling capabilities built-in, it leverages ecosystem packages, like @mswjs/data
, to give you a data-first approach to API mocking if you choose it.
import { factory, primaryKey, oneOf, manyOf } from '@mswjs/data'
// Model the data.
const db = factory({
movie: {
id: primaryKey(randomUuid),
title: String,
castMembers: manyOf('castMember'),
},
castMember: {
id: primaryKey(randomUuid),
name: String,
movie: oneOf('movie'),
},
})
// Generate request handlers based on the models.
// - GET /movies
// - GET /movies/:id
// - POST /movies
// - ...
db.toHandlers('rest')
// Including GraphQL queries!
// - query ListMovies
// - query GetMovie
// - mutation AddMovie
// - ...
db.toHandlers('graphql')
Cypress - cy.intercept()
Cypress is an end-to-end testing framework that provides API mocking capabilities through its cy.intercept()
API.
API support
API type | cy.intercept() | Mock Service Worker |
---|---|---|
REST API | ✅ | ✅ |
GraphQL API | ❌1 | ✅ |
WebSocket API | ❌ | ❌ |
1—Although it is possible to work with GraphQL requests through custom aliasing, it still requires a lot of additional setup. Cypress achieves GraphQL API mocking through HTTP handling (since GraphQL is most commonly implemented over HTTP on the web) instead of a first-class GraphQL support.
Supported environments
API type | cy.intercept() | Mock Service Worker |
---|---|---|
Node.js | ❌1 | ✅ |
Browser | ✅ | ✅ |
1—Cypress is a browser testing framework, which means you cannot use your
cy.interecept()
mocks in a Node.js process, like an integration test or a backend application.
Implementation
Environment | cy.intercept() | Mock Service Worker |
---|---|---|
Browser | Uses a browser-wide HTTP proxy to route outgoing requests through the custom server Cypress spawns as a part of its runtime. | Uses a Service Worker to intercept requests on the browser level. |
Integration
cy.intercept() | Mock Service Worker |
---|---|
Requires no changes to the code. Available natively with Cypress. | Requires no changes to the code. |
Works with any request client without additional configuration. | Works with any request client without additional configuration. |
Definition
cy.intercept()
Cypress uses a custom matcher signature and a route handler function to intercept and handle requests. It utilizes custom methods like req.reply()
, req.continue()
or req.redirect()
for more detailed request control.
cy.intercept('POST', '/users', {
statusCode: 201,
body: req.body,
delay: 100,
})
Mock Service Worker
MSW models its interception API after server-side routing and handles requests and responses according to the Fetch API specification, using the same classes you would use normally in JavaScript:
import { http, delay } from 'msw'
http.post('/users', async ({ request }) => {
// Read the request body as you would normally.
const user = await request.json()
// Control the response resolver execution flow
// via Promises, like this "delay" Promise below.
await delay(100)
// Construct a response as you would normally.
return HttpResponse.json(user, { status: 201 })
})
MSW gives you a more advanced control over the requests through its passthrough()
and bypass()
APIs, which still yield semantic HTTP responses under the hood.
Playwright - page.route()
Playwright is a browser testing tool that provides API mocking capabilities through its page.route()
API.
API support
API type | page.route() | Mock Service Worker |
---|---|---|
REST API | ✅ | ✅ |
GraphQL API | ❌1 | ✅ |
WebSocket API | ❌ | ❌ |
1—You can handle GraphQL requests with
page.route()
since they are also HTTP requests but Playwright does not provide a first-class support for mocking GraphQL APIs.
Supported environments
API type | page.route() | Mock Service Worker |
---|---|---|
Node.js | ❌1 | ✅ |
Browser | ✅ | ✅ |
1—Although with Playwright you write your tests in Node.js, Playwright itself (and its
page.route()
) only affect the traffic in the spawned browser, not the Node.js process.
Implementation
Environment | page.route() | Mock Service Worker |
---|---|---|
Browser | Uses the Chrome DevTools Protocol to intercept requests on the browser level. | Uses a Service Worker to intercept requests on the browser level. |
Integration
page.route() | Mock Service Worker |
---|---|
Does not require any changes to the code. Available natively with Playwright. | Does not require any changes to the code. |
Works with any request client without additional configuration. | Works with any request client without additional configuration. |
Definition
page.route()
The page.route()
function in Playwright utilizes custom methods like route.fulfill()
and route.continue()
to give you more control over the request.
page.route('/fruits', async (route) => {
const request = route.request()
if (request.method === 'POST') {
const response = await route.fetch()
const json = await response.json()
json.push({ name: 'Playwright' })
return route.fulfill({ response, json })
}
route.continue()
})
Mock Service Worker
import { bypass } from 'msw'
http.post('/fruits', async ({ request }) => {
const response = await fetch(bypass(request))
const json = await response.json()
json.push({ name: 'Mock Service Worker' })
return HttpResponse.json(json, response)
})
The alternative for
route.continue()
in MSW is simply returning nothing from a response resolver. But since we can narrow down the request interception to both the method (POST
) and path (/fruits
), our request handler never concerns itself with other requests.
MSW uses the custom bypass()
function that wraps any given Request
instance to prevent it from being affected by any other otherwise matching request handlers.