[React] Unit Testing APIs with Mock Service Worker (MSW)

When doing Test-Driven Development with React, writing tests can be more difficult once the APIs and the HTTP calls are required.

When I first tried doing TDD, I tried to test everything using jest.mock(). Jest was great for testing the behaviors but the test code became messy due to a lot of stubbing with mockResolvedValue() for every single API needed across multiple components.

It can be noticed that half of the test code is about setting up the required stub response to run my test successfully. Would it help if I can reuse some of the stub responses across different tests? One can argue reusing and having stubs in a different place may make it harder to follow when tests go wrong. However, it can be imagined from seeing the code below that the test code will be flooded with stubs.

Let us take a look at another test case.

Here the return value of axios.get() is stubbed. This could be because we don’t want to make a network call for unit tests and we want to have control over what we test. However, with this approach there are some concerns. First of all, the test cases and the stub responses have to worry about the response schema of axios or equivalent HTTP client. In addition to that there is no check on if I am using the HTTP client properly either.

MSW

This led me to msw (Mock Service Worker).

msw creates a mock server which intercepts all network requests and return the handlers (including stub responses) defined by you.

This will give you a few benefits:

  • Default handler can be defined and specific handlers can be used to override in specific tests.
  • No need to make assumptions on the response schema of the HTTP client.
  • Tests will fail if HTTP client is not being called appropriately.

Let us look at an example using React + Typescript.

Step 1: Install

$ npm install --save-dev msw

Step 2: Define handlers

Step 3: Setup Server

Step 4: Setup Tests

Step 5: Write Tests

We will look at the same two tests above to see how they are changed.

For the first test case, it can be noticed that the response that was stubbed with mockResolvedValue is now replaced with mswServer.use().

This is because the test overrides the default handler defined as handlers in handlers.ts. If the default handler can be used, mswServer.use() can be removed. This reduced the test code and also makes it possible for other component’s reuse the handlers whether they use default handlers or override handlers.

Let us look at the next test case. Again, I overrode the handler response.

Not much difference compared to the first test case. Interesting fact is that when I output the result of axios.get(), it prints like something this. It is nice that it gives details of the axios response schema.

Conclusion

There is no right or wrong approach. Making a sample using msw was a fresh experience for me because it allowed me to reuse responses, not having to worry about response schema, and extra safety on the usage of HTTP client.

--

--

--

Software Engineer @ VMware Tanzu Labs

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Rich Text Editor for iOS using WKWebView

Building a ready for production microservice in NestJS: Writing an authorization microservice —…

How I developed a Concurrent Mode friendly library for React Redux

Angular 11 Firestore CRUD: add/get/update/delete documents with AngularFireStore

Upload Any File Into Google Drive With Fastify and Googleapis Node Package

Server Side JSON Web Token Implementation with PostgreSQL and Node.

Animating SVG

LeetCode Algorithm Challenge: Needle in a Haystack

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
J.Kim

J.Kim

Software Engineer @ VMware Tanzu Labs

More from Medium

scaling nodejs based micro services

scaling-microservices

Caching database queries in TypeScript

TDD — Unit Testing TypeScript project with Jest

Avoiding Circular Dependency Issues in Nest.js