Hey! Thank you for deciding to contribute to Mock Service Worker! This page will help you land your first contribution by giving you a better understanding about the project's structure, dependencies, and development workflow.
Getting yourself familiar with the tools below will substantially ease your contribution experience.
Mock Service Worker depends on multiple other libraries.
Library name | Purpose |
---|---|
cookies | Enables cookies persistance and inference between environments. |
headers-utils | Headers polyfill to manage request/response headers. |
interceptors | Provisions request interception in Node.js (internals of setupServer ). |
node-match-path | Matches a request URL against a path. |
There are cases when an issue originates from one of the said dependencies. Don't hesitate to address it in the respective repository, as they all are governed by the same team.
Please use the GitHub UI to fork this repository (read more about Forking a repository). Mock Service Worker has forked builds enabled in the CI, so you will see the build status of your fork's branch.
$ cd msw
$ yarn install
$ yarn start
Please use Yarn while working on this project.
# Checkout the default branch and ensure it's up-to-date
$ git checkout main
$ git pull --rebase
# Create a feature branch
$ git checkout -b feature/graphql-subscriptions
# Commit the changes
$ git add .
$ git commit
# Follow the interactive prompt to compose a commit message
# Push
$ git push -u origin feature/graphql-subscriptions
We are using Conventional Commits naming convention. It helps us automate library releases and ensure clean and standardized commit tree. Please take a moment to read about the said convention before you name your commits.
Tip: running
git commit
will open an interactive prompt in your terminal. Follow the prompt to compose a valid commit message.
Once you have pushed the changes to your remote feature branch, create a pull request on GitHub. Undergo the process of code review, where the maintainers of the library will help you get the changes from good to great, and enjoy your implementation merged to the default branch.
Please be respectful when requesting and going through the code review. Everyone on the team is interested in merging quality and well tested code, and we're hopeful that you have the same goal. It may take some time and iterations to get it right, and we will assist you throughout the process.
There are two levels of tests on the project:
- Unit tests, cover small independent functions.
- Integration tests, test in-browser usage scenarios.
Always begin your implementation from tests. When tackling an issue, a test for it must be missing, or is incomplete. When working on a feature, starting with a test allows you to model the feature's usage before diving into implementation.
Unit tests are placed next to the tested code. For example, if you're testing a newly added multiply
function, create a multiple.test.ts
file next to where the function is located:
$ touch src/utils/multiply.test.ts
Proceed by writing a unit test that resembles the usage of the function. Make sure to cover all the scenarios
// src/utils/multiply.test.ts
import { multiply } from './multiply'
test('multiplies two given numbers', () => {
expect(multiply(2, 3)).toEqual(6)
})
Please avoid nesting while you're testing.
Once your test is written, run it in isolation.
$ yarn test:unit src/utils/multiply.test.ts
At this point, the actual implementation is not ready yet, so you can expect your test to fail. That's perfect. Add the necessary modules and logic, and gradually see your test cases pass.
$ yarn test:unit
We follow an example-driven testing paradigm, meaning that each integration test represents a usage example. Mock Service Worker can be used in different environments (browser, Node.js), making such usage examples different.
Make sure that you build the library before running the integration tests. It's a good idea to keep the build running (
yarn start
) while working on the tests. Keeping both compiler and test runner in watch mode boosts your productivity.
In-browser integration tests are powered by the page-with library (Chrome automation tool) and still run in Jest. These tests usually consists of two parts:
[test-name].mocks.ts
, the actual usage example.[test-name].test.ts
, the test suite that loads the usage example, does actions and performs assertions.
Let's write an integration test that asserts the interception of a GET request. First, start with the *.mocks.ts
file:
// test/rest-api/basic.mocks.ts
import { rest, setupWorker } from 'msw'
const worker = setupWorker(
rest.get('/books', (req, res, ctx) => {
return res(
ctx.json([
{
id: 'ea42ffcb-e729-4dd5-bfac-7a5b645cb1da',
title: 'The Lord of the Rings',
publishedAt: -486867600,
},
]),
)
}),
)
worker.start()
Notice how there's nothing test-specific in the example? The
basic.mocks.ts
file is a copy-paste example of intercepting theGET /books
request. This allows to share these mocks with the users as a legitimate example, because it is!
Once the *.mocks.ts
file is written, proceed by creating a test file:
// test/rest-api/basic.test.ts
import * as path from 'path'
import { pageWith } from 'page-with'
test('returns a mocked response', async () => {
// Create a Chrome instance with the usage example loaded.
const runtime = await pageWith({
example: path(__dirname, 'basic.mocks.ts'),
})
// Dispatch a "GET /books" request.
const res = await runtime.request('/books')
expect(await res.json()).toEqual([
{
id: 'ea42ffcb-e729-4dd5-bfac-7a5b645cb1da',
title: 'The Lord of the Rings',
publishedAt: -486867600,
},
])
})
Read more about all the
page-with
features in its documentation.
Integration tests showcase a usage example in Node.js and are often placed next to the in-browser tests. A Node.js integration test file has the *.node.test.ts
suffix.
Similar to the browser tests, these are going to contain a usage example and the assertions over it. However, for Node.js tests there is no need to create a separate *.mocks.ts
file. Instead, keep the usage example in the test file directly.
Let's replicate the same GET /books
integration test in Node.js.
// test/setup-server/basic.test.ts
import fetch from 'node-fetch'
import { rest } from 'msw'
import { setupServer } from 'msw/node'
const server = setupServer(
rest.get('/books', (req, res, ctx) => {
return res(
ctx.json([
{
id: 'ea42ffcb-e729-4dd5-bfac-7a5b645cb1da',
title: 'The Lord of the Rings',
publishedAt: -486867600,
},
]),
)
}),
)
beforeAll(() => server.listen())
afterAll(() => server.close())
test('returns a mocked response', async () => {
const res = await fetch('/books')
expect(await res.json()).toEqual([
{
id: 'ea42ffcb-e729-4dd5-bfac-7a5b645cb1da',
title: 'The Lord of the Rings',
publishedAt: -486867600,
},
])
})
$ yarn test:integration
$ yarn test:integration test/rest-api/basic.test.ts
Build the library with the following command:
$ yarn build
Learn more about the build in the Rollup configuration file.