React Testing using Jest along with code coverage report

Yogesh Chavan
Jan 28 · 10 min read

Learn how to use Jest and Enzyme to easily test react applications

Photo by Ferenc Almasi on Unsplash

Writing test cases is an important part of React development. Each component should have test case written correctly so by just running a single command we can make sure that our entire application works correctly no matter how big the application is.

So if some API call is failing because server is down or there is some issue processing request we can easily catch it before the application is deployed to production by just running a single command.

Therefore writing unit tests in React is very important and every React developer should know how to write the test cases.

In this article, we will explore all of that.

So let’s get started

To start with, clone the todo list repository code from HERE which I created to demonstrate localStorage in this article

Once cloned, run the application using

1. npm install
2. npm start

Now we will start writing test cases using Jest Framework.

When using create-react-app, jest is already included so there is no need of installing it separately as in our case.

We will write simple test case to understand jest and then we will start writing test cases for the todo app.

Create a new folder named tests inside src folder and add a simple test file with name app.test.js with following code

// app.test.js
const add = (a, b) => a + b;
test('should add two numbers', () => {
const sum = add(3, 4);
expect(sum).toBe(7);
});

Note: Every test file should end with .test.js extension. This is how jest is able to find all the test files to be executed.

Let’s understand the above code.

Here we are testing add function which takes two numbers and returns sum of those numbers. Jest provides some global functions one of them is test function inside which we write our test case.

You can find complete list of global functions HERE

The test function takes two arguments.

1. the string which describes what we are testing
2. the function where we write our test case

Inside the test function, we are calling the add method by passing two numbers and then checking if the sum of 3 and 4 equals 7.

To run this file, open command line or terminal and execute

npm run test

Once executed, you will see that our test case is passed

Now change the expect statement from

expect(sum).toBe(7);

to

expect(sum).toBe(8);

and you will immediately see that the test case has failed because 3 + 4 is definitely not 8 and you will see the filename along with line number where it fails. That’s how easy to test and debug with jest.

toBe is one of the many methods provided by Jest for testing. You can find the complete list HERE
Change the code from 8 to 7 to make it work again.

Now, let’s start testing our todo list app. To do that we need to install the enzyme library and make some configuration changes.

To easily test components and handle the event handlers, manage state etc, Enzyme library is used together with Jest.

Install enzyme and it’s dependencies using

npm install --save-dev enzyme@3.11.0 enzyme-adapter-react-16@1.15.2 jest-enzyme@7.1.2

If you are using react less than version 16, you need to install it’s compatible version of adapter as shown HERE

To use Enzyme with jest, create a new file setupTests.js inside the src folder and add the following content

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import 'jest-enzyme';
configure({ adapter: new Adapter() });

To connect it with Jest create a file with name jest.config.json in the root of the project where package.json file is present and add following content

{
"setupFiles": ["<rootDir>/src/setupTests.js"]
}

Now we are done with the configuration, let’s start testing the Header component from src/components directory.

Snapshot Testing:

Snapshot testing is a way to test react components where we create a snapshot of the component by passing the props if any.

It’s usual practice to create the same folder structure for test files as components inside the src folder so create components folder inside src/tests folder and create a new file Header.test.js inside it and add the following code

import React from 'react';
import { shallow } from 'enzyme';
import Header from '../../components/Header';
test('should test Header component', () => {
const wrapper = shallow(<Header />);
expect(wrapper).toMatchSnapshot();
});

Here, we are using shallow rendering to create a snapshot of the Header component.

Now, if you run the tests again using

npm run test

you will see that, all tests passed and a new folder with name __snapshots__ is created inside components which has the Header component snapshot saved to the file

Now open the src/components/Header.js file and change something in the file like add an exclamation mark in h1

import React from 'react';const Header = () => {
return <h1>Todo List App!</h1>;
};
export default Header;

Now, if you save the file, you will see an error as shown below

The error clearly tells us that, the new snapshot with exclamation mark does not match the original snapshot saved in Header.test.js.snap.

Here we have two choices

1. Keep the change we did in Header.js (added exclamation mark)
2. Discard the change in Header.js

So if you press w key in the terminal you will see more options

If we want the change in Header.js, press u key and the snapshot will be updated with our changes in Header.js file and you will see the updated snapshot message

If you don’t want the exclamation mark in the Header.js, remove it from the file and then save the file which will re-run the tests and will keep the old snapshot without exclamation mark.

So using snapshot testing we can easily test our react components and track if something changes in any of the file may be by mistake or by purpose.

Now, let’s write test cases for src/components/TodoLists.js component.

Create a new file TodoLists.test.js inside src/tests/components folder

The TodoLists.js component accepts props which is an array of todos, So let’s write test case which takes an array as props

import React from 'react';
import { shallow } from 'enzyme';
import TodoLists from '../../components/TodoLists';
test('should test TodoLists component with default state of empty array', () => {
const wrapper = shallow(<TodoLists todos={[]} />);
expect(wrapper).toMatchSnapshot();
});

If your npm run test command is still running, you can see a new snapshot is created into __snapshots__ folder

So you can see that, the empty array check from TodoLists.js is correctly getting executed.

Now, we will write another test case by adding some todos

test('should test TodoLists component with list of todos', () => {
const wrapper = shallow(
<TodoLists todos={['Read Newspaper', 'Publish Article']} />
);
expect(wrapper).toMatchSnapshot();
});

As you can see the snapshot is updated with the new test case data

Now we have handled both the conditions from TodoLists.js

Let’s move on to next component which is TodoItem.js component

It accepts a single todo as a prop

Create new file TodoItem.test.js inside src/tests/components folder and add the following code inside it.

Let’s start with testing AddTodo.js component now

Create new file AddTodo.test.js inside src/tests/components folder

AddTodo component accepts a handleAddTodo method as props

Let’s test AddTodo component by not passing any props

when you run npm run test you will see a new snapshot file created for this

Now let’s add another test case to handle the onSubmit method call

When we submit the form, handleAddTodo method is called so we will be creating a spy in jest to test that function call.

Creating a spy is a way of creating a function and checking if the written method gets called or not.

To create a spy for function in Jest, we use jest.fn method provided by Jest and we will pass it as the handleAddTodo prop

Then we will find the Form element and we will trigger the onSubmit handler.

It’s always good to save the snapshot before, so it will be easy to identify what is the final element we need to use in .find method.
we will use the simulate method of enzyme by mentioning which event to trigger.

Once the submit event is triggered, we will verify if it’s actually triggered or not by calling toHaveBeenCalled method of jest

You can find all other methods provided by enzyme HERE

If you run the tests, you can see that a new snapshot is saved and our test runs successfully.

Feel free to open the AddTodo.test.js.snap file and understand what gets stored when we create a new snapshot for this component which is very important part of writing test cases.

Now we will add tests for the final component which is TodoList.js

Create new file TodoList.test.js inside src/tests/components folder

It’s snapshot will look like this

Now, we are done with all component test cases. Let’s explore how to see the total code coverage for the entire application.

For this we will need to make some configuration changes.

Open jest.config.json and add the following content

{
"setupFiles": ["<rootDir>/src/setupTests.js"],
"testRegex": "/*.test.js$",
"collectCoverage": true,
"coverageReporters": ["lcov"],
"coverageDirectory": "test-coverage",
"coverageThreshold": {
"global": {
"branches": 0,
"functions": 0,
"lines": 0,
"statements": 0
}
},
"moduleDirectories": ["node_modules", "src"]
}

Here we are telling jest to generate coverage reports for all files ending with .test.js as shown in testRegex property.

Now to generate coverage report, execute following command from terminal

npm run test -- --coverage --watchAll=false

Once executed, we will see the total coverage for each test file

This will also create a new coverage folder in the project

If you open the index.html file in browser located at coverage/lcov-report folder you will be able to view the report

As you can see, we have covered all the scenarios for each of the component except TodoList.js for which it’s showing only 29.41% statements covered.

Let’s open the todoList.js and we can see what extra test cases we need to handle

As you can see, the lifecycle methods and handleAddTodo method is not handled in our test cases so let’s add the test cases for them

Open TodoList.test.js from src/tests/components folder and add following test case in it.

test('should call handleAddTodo method', () => {
const wrapper = shallow(<TodoList />);
const instance = wrapper.instance();
const value = 'Publish Article';
instance.handleAddTodo({
preventDefault: () => {},
target: {
todo: {
value
}
}
});
});

Here we are manually calling the handleAddTodo using instance.handleAddTodo method. The handleAddTodo is expecting an event object as parameter which has preventDefault method and value property so we are proving it as a parameter

Now run the coverage command again

npm run test -- --coverage --watchAll=false

and you will see the coverage percent has increased for TodoList.js

If you open the TodoList.js file, you will see that handleAddTodo method is covered but the componentDid mount is still not covered by test cases because of which the percentage is 88.24% and not 100%

I will leave it to you to add the test case for it as an exercise for you.

Hint: you can use mount method of enzyme to handle that test case

Couple of improvements:

If you open the TodoList.test.js file, you can see that we have duplicate code of const wrapper = shallow(<TodoList />);
It’s repeated in first and second test case.

If we add more test cases it will be repeated again. So jest provides some life cycle methods using which we can remove the duplicate code.

Life cycle methods:

1. afterAll: It will be executed after all the test cases from the test file are finished executing
2. beforeAll: It will be executed before executing any of the test case in a file
3. afterEach: It will be executed after each of the test case in a file
4. beforeEach: It will be executed before each of the test case in a file

Complete List: https://jestjs.io/docs/en/api

we will use the beforeAll life cycle method to fix the duplicate code

That looks much better now..

Github Source Code: https://github.com/myogeshchavan97/todolist_react_tests

That’s it for today. Hope you learned something new today.

Subscribe to get weekly updates directly in your inbox https://subscribe-user.herokuapp.com/

The Startup

Medium's largest active publication, followed by +589K people. Follow to join our community.

Yogesh Chavan

Written by

Full Stack Developer | Javascript | React | Nodejs. Subscribe to get weekly updates directly in your inbox https://subscribe-user.herokuapp.com/

The Startup

Medium's largest active publication, followed by +589K people. Follow to join our community.

More From Medium

More from The Startup

More from The Startup

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade