JavaScript testing #13. Mocking a REST API with the Mock Service Worker

JavaScript Testing

This entry is part 13 of 14 in the JavaScript testing tutorial

One of the most crucial things about writing tests is that they should be deterministic. A particular test should always succeed or always fail. To achieve that, we sometimes need to mock parts of our environment when testing. A good example can be a function that relies on pseudo-random numbers.

Besides the above, we also want our frontend unit tests to be independent of the backend. If there is a bug in our API, we don’t want it to cause frontend tests to fail too. To achieve that, we could mock functions we use to make HTTP requests, such as and . While that would work fine, switching from using to or the other way around would force us to adjust our mocks. So instead, the Mock Service Worker library offers a different approach.

Introducing Mock Service Worker (MSW)

The Mock Service Worker library offers two major features. One of them is mocking the API requests in the browser.

In the callback for the function, we have three arguments:

  • : an object that stores the information about the matched request,
  • : a function that creates a mocked response object,
  • : an object containing a set of response transformers that help us compose a response.

The function accepts the response transformers as arguments. Each response transformer modifies the response. Besides using the response transformers provided by the object, we can create our own.

The function creates a Service Worker configuration instance and the function registers and activates it. Doing the above causes the MSW library to intercept the API request through a Service Worker and return the response we’ve defined. This means we can open the developer tools and see the browser making the API requests. It is a significant improvement from mocking the function.

If you want to know more bout Service Workers in general, check out The basics of Service Workers: how should our application behave when offline?

The second major feature that MSW offers is mocking API requests in Node.js.

We can pass multiple arguments to the and functions to mock multiple requests.

It is essential to realize that Node.js does not have a concept of Service Workers. Because of that, MSW has to intercept functions such as and being called.

The use-cases of client-side and Node.js mocking

The first thing to notice is the API for defining both Node.js and browser mocks is the same. Therefore, sharing the mocks between the browser and Node.js is easy.

Mocking client-side API requests might come in handy in a few scenarios. One of them is when the actual API is not ready yet, but we still want to be able to work on the frontend side of our application. This can be a common case in teams where different developers are working on creating the API and on the frontend.

Mocking API requests in the browser can also be helpful if we don’t want our Cypress tests to use a real API. However, we should know that such tests might not be considered end-to-end tests because they don’t verify all of the aspects of the application.

Writing tests with Jest involves running our frontend application through Node.js. Because of that, Node.js API mocks can be crucial to our unit tests. Therefore, in this article, we focus on writing Node.js mocks.

Mocking API calls in a Node.js environment

We can take two different approaches when setting up mocks. Each of them has its advantages and disadvantages.

Setting up API mocks once for all of the tests

The most straightforward way of mocking API calls with MSW is to configure it once for all of the tests. To do that, we need to define the server in a file included in the array in our Jest configuration.

setupTests.ts

In Create React App, is the default file used for configuring tests.

To verify the above approach, let’s create a straightforward component for displaying a list of posts.

PostsList.ts

Our React component uses a custom hook that sends an HTTP request to the API we’ve mocked.

usePostsList.ts

Now, let’s write a test to ensure we render the correct data.

PostsList.test.ts

PASS src/PostsList/PostsList.test.tsx
The PostsList component
✓ should display the tiles of all of the posts
✓ should display the bodies of all of the posts

Implementing the above approach has some advantages. It is straightforward to use and keeps our tests short and easy to understand. Unfortunately, it does not give us much control over the API mocks. Our component can display an error message if the API request fails. Unfortunately, we currently don’t have a way of testing that.

Setting up API mocks per test

Instead of setting API mocks once for all of the tests, we can do it once per test instead. It gives us a lot more control over how our API mocks work.

PostsList.test.ts

PASS src/PostsList/PostsList.test.tsx
The PostsList component
if fetching posts is a success
✓ should display the titles of all of the posts
✓ should display the bodies of all of the posts
if fetching posts fails
✓ should display an error message

In the above test, we can mock the response from the same endpoint in more than one way. Thanks to that, we can test how our React component behaves in many different scenarios.

While this approach is more powerful, it makes our tests noticeably more complicated. Also, starting the server multiple times adds to the execution time of our tests. The additional control seems to be worth it, though.

Intercepting the request

We sometimes want to assert the request payload, for example, to ensure that our frontend made a valid HTTP request to create a post. To test it, let’s create a simple form.

PostsForm.test.ts

When the user clicks the button, we send a POST request to the API.

usePostsForm.test.ts

One way to test the above logic would be to create a variable in the test that we modify in our route handler.

PostsForm.test.ts

PASS src/PostsForm/PostsForm.test.tsx
The PostsForm component
when the user types valid values for body and title
and when the user clicks on the submit button
✓ should make a POST request with the form data

We can also use the life cycle events built into MSW to achieve a similar result.

PostsForm.test.ts

The official documentation shows also shows how to create a function.

Summary

In this article, we’ve gone through the Mock Service Worker library. We’ve learned about different use cases for it – both in the browser and through Node.js. We’ve also used it in various testing scenarios using React Testing Library and Jest. This included learning different ways to set up API mocks to match our needs. Thanks to practicing all of the above, we’ve grasped the basics of working with MSW.

Series Navigation<< JavaScript testing #12. Testing downloaded files and file inputs with CypressJavaScript testing #14. Mocking WebSockets using the mock-socket library >>
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments