Mocking Requests with Mock Service Worker

I’ll explain the Pokemon in a minute. But first, we all agree unit testing is not the most fun! Particularly testing API requests. However, I recently discovered Mock Service Worker, a simple and efficient way of mocking axios and fetch in testing. Simple because of the way it integrates into applications, and efficient because it operates at a network level, making it function synonymously with an actual mocking server.

Taken from the npm documentation, here are some of the ways MSW behaves differently than other mocking libraries:

  • Intercepts requests on the network level, not the application level.
  • If your think of your application as a box, Mock Service Worker lives in its own box next to yours, instead of opening and altering it for the purpose of mocking.
  • Agnostic of request-issuing libraries, so you can use it with fetch, axios, react-query, you-name-it.
  • The same mock definition can be reused for unit, integration, E2E testing, and debugging.

MSW in action

Okay, let’s show an example of an axios.get request mocked with Mock Service Worker. For this, I‘ll fetch data from the (very professional) REST API, PokeAPI!

If you’d like to follow along, go ahead and add the msw module in your package.json file as a dev dependency with npm i msw --save-dev .

The fetchData() function above makes an axios.get request to PokeAPI and returns the name property of a Pokemon in the response (this example returns the name ‘venusaur’). Simple enough. Now let’s look at how this is mocked in our test:

The first few things to import to the test file are {rest} from ‘msw’ and {setupServer} from ‘msw/node’. We then create a server variable and set it equal to the setupServer function. Essentially what setupServer is doing is:

  1. setting up a server — as you probably guessed — that listens for any requests made to the provided API,
  2. intercepts them, and
  3. handles how it should respond.

This is how MSW functions at the network level. The second thing to pass to setupServer is a callback function that takes the req, res, ctx arguments. ctx is the context used to help build out the responses we want back. For instance, the status of the response can be set with ctx.status(200).

In this case, we return a json object with ctx.json({ name: 'pikachu' }) .

From here the mocking server still needs some additional configuration with a few Jest callbacks. On line 15, beforeAll(() => server.listen()) tells the server to start listening. afterAll(() => server.close()) tells the server to stop listening once the tests are complete.

Now we are ready to write our test! We call await fetchData() and store the value in pokemon. The assertion is for (pokemon).toEqual('pikachu'). The real PokeAPI server returns the name ‘venusaur’. However, Mock Service Worker intercepts the request and returns ‘pikachu’ in the response, therefore the test passes!

Tip: The ctx.json() object in the mocked response needs to match the format of what’s returned by fetchData(). Since fetchData returns res.data.name, the mocked response also needs to return a name property.