React testing with Jest and Enzyme: a simple example walkthrough

Abhishek Chakraborty
5 min readDec 14, 2020

--

This article will be walking through on writing unit and integration tests for different React Components of a demo multi-page app. Our tests will use Jest, Enzyme, and the NPM module fetch-mock. The app in question is a basic bare-bone todo app, where a user can login with credentials and add/delete items from the list which appears on their home page. For simplicity, my app has pre-set two users, and so will not feature a sign-up page. All code for this mock app can be found on my GitHub. Here is a demo of this app:

Note: unlike a test-driven development (TDD) workflow, I pre-coded the app’s React Components. I believe this is a realistic way for one to familiarize themselves with the subtleties of writing these tests. Once you are comfortable, you can take the next step and switch the order of these actions: writing well-defined React code based on your tests!

Also Note: I am myself relatively new to React testing, so while these steps are currently suitable for me, they might not be the most optimal approach for your apps.

A little background

Automated testing has become a standard part of any established software development cycle. We have all heard that buzz-word: test-driven development (TDD). To briefly define these terms: automated testing is where developers write code that verifies the expected behaviours of implemented features. In TDD, test code is written just prior to writing feature code which ensures that your code works just exactly as you intend. While merging features, this cumulative series of tests (created by several team developers) is run automatically (usually as part of the deployment pipeline), ensuring that each merge continues to maintain the entire app functionality. I usually develop web-apps through a partitioned strategy, consisting of an isolated Flask (Python) REST API backend application-server handling requests from a React frontend web-server. In this app structure, the ideal goal would be to achieve a comprehensive set of Integration Tests. For brevity, I won’t go into the details of Integration and Unit tests as there already exists a lot of discussion/resources.

Until recently my experience with testing had been primarily with unit-testing Python code using pytest. This extends well into integration-testing Flask endpoints. I find pytest to be intuitive and also find that there is good documentation/resources for it, like this blog post covering how to test file-uploads for Flask endpoints.

Naturally, I wanted to continue venturing into testing the React portion of my web-apps. There are quite few popular React-testing libraries, notably: react-testing-library, Jest and Enzyme. Upon exploring through each of these, I settled on testing through a popular powerful combination of Jest and Enzyme. If you are brand new or just need a refresher on the theoretical concepts of React code testing using Jest & Enzyme, this Medium post, does an absolutely fantastic job. The rest of this article assumes that you are aware of these concepts. Let’s jump to code!

App.test.jsx: testing the central App component

The first component we will write tests for will be App. In our React code, the App component renders the NavBar and upon mounting, makes an HTTP request to the application-server to verify current user authentication status. If authenticated, it renders the Home component (via routes), otherwise, it renders the Login component (also via routes). This App component is itself rendered by React-DOM in index.jsx. Let us begin by brainstorming testing strategies for App:

If the User is not authenticated upon App mounting, we may assert expectation tests in this order:

  1. Verify that the Login component is displayed with its own functional components (username, password, login button).
  2. Verify that the NavBar component is displayed, however without the. ‘Logout’ button.
  3. Verify that the Home component is not displayed at all.
  4. Mimic a valid user login by simulating changes to the aforementioned functional components (username, password, login button) and verify that DOM elements are updating as expected.
  5. Repeat the step above but with an invalid user login.

If the user is correctly authenticated upon App mounting, we may assert expectation tests as above, just in the logical opposite fashion

Below is a quick look at the code for a subset of the above cases, specifically step 5, when the user is not authenticated upon App mounting:

Interesting takeaways:

  • Notice how we mocked the window.alert method on line 8? Not doing so, caused Jest to throw “Error: Not implemented: window.alert”. Jest’s test environment is similar to a browser and so it implements most methods developers expect on a browser. Except, now and then you may make use of such a method in your code that is not implemented, causing this error. In this case, simply mock the function’s expected behaviour. In my case, I did not plan to test a specific behaviour of the alerts.

Home.test.jsx: testing the tasks home

The next component we will explore writing tests for is the Home component. Even within our basic app, our Home component actually has several verifiable events taking place. So let’s brainstorm a some tests:

Testing correct DOM/State updates of tasks:

First, let’s see if there are any specific actions which we should expect when our Home component mounts. Indeed, under componentDidMount(), we make a request to fetch a welcome message as well as the user’s existing tasks. We then update our component’s states and as a result update our DOM. So for each of our test cases, we shall begin with a simple layout: rendering 2 existing tasks “Do laundry” and “Mow lawn” for the user sampleUsername. Let’s look at the code setup for this:

Next, let’s consider our two test cases: adding and deleting a task. The tests structure for these two are very similar, so here we will take a look at only addition:

Interesting takeaways:

  • Notice what’s going on in lines 15 & 16? The fetchMocks here are different than ones we have been implementing thus far. Here, I ran into a specific issue: I wanted to mock and call handleTaskAdd, which is an asynchronous function that makes an HTTP request, and upon resolving the request’s promise successfully, calls the asynchronous fetchTasks function which itself makes another HTTP request to fetch updated task values. After much research and trail/error, I decided to not implement this via Jest’s regular mockImplementation on the fetch function but instead, to make use of the npm fetch-mock module.
  • Despite this module, I was unable to directly simulate an add task action by simulating a click on the respective button. I found specifically through log-testing during the tests that in the environment runtime the correct actions took place, i.e. my state tasks was updated to include the newly inputted task. However, for some reason, this update would not update on my wrapper component. Perhaps I just need to research further on this, but this event certainly highlights some of the more ‘finicky’ elements of these React testing libraries.

So we have gone through some useful test cases for our basic todo app. Again, you can find more detailed tests and other demo code on my GitHub. So what’s next? Well these tests are great, but they can only be run locally by a developer. As such they provide ineffective cover for catching breaks when merging new feature code in or in performing checks before re-deployment. The way to achieve these is through integrating the tests as part of your project DevOps pipeline, so that you may merge and re-deploy your apps with assurance. That’s what we’ll be exploring in the next article (check out my new linked article)!

--

--