Testing asynchronous code with Jest and Testing Library React

Pablo Rodrigo Darde
Ship It!
Published in
5 min readMar 13, 2020
Here the origin of the term (Patch) in software engineering. Used to fix computer programs on punch cards.

This is a Post in Tutorial format that aims to explain the basics of testing asynchronous code on the frontend through the use of Test Doubles such as Mocks and Stubs.

Testing asynchronous code on the frontend is simple but requires a few points of attention. Unlike Enzyme, with Testing Library React we can test our code focusing on the behavior of our application instead of detail implementation.

I’ll assume you have a bit of knowledge about react and testing. You can check more about React, Jest, and Testing Library React before continuing.

Creating a Posts Reader Application

Let’s take as an example an app that fetches a list of posts from jsonplaceholder API. You can checkout the application on GitHub.

Let’s start creating an app with create-react-app scaffold by typing the following command in the terminal.

$ npx create-react-app my-posts-application

Now, replace the App.js file content with the following content.

src/App.js

If you run the app you’ll see a Loading… message.

Let’s start writing our first test.

We’re using the getByText helper (from testing library react) to get the element with text loading and expect it should be in the DOM Document.

Creating a fetch posts service and fixing our test

Now, let’s create our fetch service. Create a services folder under the src directory, and then, create the fetchPosts.js within.

Our fetch service will simply fetch a list of posts.

src/services/fetchPosts.js

As you can see, this is an asynchronous function that fetches a list of posts from jsonplaceholder API and returns an array of posts objects.

Let’s update our App.js file. In order to fetch the posts through our service.

We’re using the useEffect hook to call our fetch service. While the fetch process is running, the app shows a loading span, when done, it shows a list of posts. However, our test is now showing a warning.

It’s happening due to our component is executing an asynchronous operation and our test is synchronous. To solve this warning let’s unmount our component right after we ensure the loading message is in the document. To unmount the component we call the cleanup helper from testing-library/react.

In the code above, we unmounted our component on line 11. However, now we got a “Can’t perform a React state update on an unmounted component” warning. This occurred because, although we have called the cleanup helper, our real component is still running the side effect. Let’s fix it returning a cleanup function at the end of our useEffect hook, analog as we did with componentWillUnmount in React classes.

Now, our component only updates its state if the component is mounted.

We can see our application rendering a list of posts. Let’s inspect what our fetch service returns.

fetchPosts.js returns an array of post objects.

Creating a mock of Axios

To test our service we’ll need to create a mock of Axios's get method, a fake version of the Axios dependency. External dependencies cannot interfere in our test. Imagine you’re testing a payment API. You’ll not want to make a real pay operation during your tests, or even you’ll not want to exceed your API request limits for testing purposes. To solve this, we will create a mock.

With Jest is easier to create mocks. To mock npm packages, just create a __mocks__ folder adjacent to node_modules folder (or where you defined your project’s root directory). When running your tests Jest automatically detects all mocked npm dependencies and uses them instead of the real versions.

src/__mocks__/axios.js

Write our asynchronous test

Axios’ get method returns a promise, our mock returns a promise as well with an object that looks like the real response object. Remember, we don’t want to test Axios, we want to test our service.

Let's write a test for the fetch process.

src/App.test.js

Note that now we have an asynchronous test. We set the async word in our it function. We also need to wait for the list to be rendered. So we call the waitForElement helper with await. We’re also using the getByTestId helper. In order to use this, we should add a data-testid attribute in our inspected element.

src/App.js — data-testid attribute

Line 28: data-testid attribute used in the test.

Bonus

I know this post has grown a lot. But what about checking how to test a button that shows only the first ten posts?

Let’s do a TDD here. We’ll now create a test for the button that doesn’t exist yet.

src/App.test.js

Obviously, as you can see above, our test is failing. Now, create the button with the “Filter” label.

src/App.js

Nice! Just created a React fragment <>…</> and put our button inside. Now, let's test its functionality.

You can remember our Axios mock just return only two results. In order to fetch the first ten results, we need to create a new fake list of results (stub) to rewrite our Axios’ mock implementation. Let’s do it.

src/App.test.js

Now, we should rewrite the Axios mock.

At first, let’s import the mocked Axios in our test.

import mockedAxios from 'axios';

Then, rewrite the mock with mockResolvedValueOnce.

src/App.test.js

Good, our mock is returning our fakeList. Let’s implements the feature.

src/App.js

All tests passed! Note we created a new method on line 21. And called it on the onClick button handler. If you check your application, clicking the button will filter the first ten results.

That's it! I hope you enjoyed this post.

--

--

Pablo Rodrigo Darde
Ship It!

Software Engineer — bachelor’s degree in Software Engineering.