Adding Unit Test to your React Application (Jest & Enzyme)

Test your app and build confidence

Being able to build something is awesome, but we need to always make sure that what we build works! Sure we can test everything one by one, but why bother doing that when we can have an application that can test it for us.

I’ve created a simple project in my GitHub repository for you to download. If you already have a project running, we will be mainly modifying the package.json.

A few word about Unit Test

I am a firm believer on unit test, in fact I try to practice (and enforce) Test Driven Development (TDD) in my work place. Why? well for one, once you are familiar with your testing framework, development of applications gets faster as you don’t need to wait for your application to compile and deploy per every change, and have a high confidence on how things are running. A well written Unit Test gives you visibility for potential breaking changes — this ensure no regression can happen when you make changes to your function.

Unit Testing with React

Since React.js can act as both the presentation layer and the business logic layer of your application, we will configure our test to run both. Within the presentation layer, we want to ensure that all the html elements we said the component is going to be built is present, and properties set is reflected in the DOM. For the business logic layer, we will test whether the DOM reacts to interactions that we configure.

As expected, lets install the libraries we need to run our unit test

npm i enzyme enzyme-adapter-react-16 jest react-test-renderer --save-dev

we will be using Jest and Enzyme for our testing framework since they work well with React. To run our test, lets configure it inside our package.json

"scripts": {
...
"test": "jest",
...
}

Once this is configured, we can easily run our test by running

npm run test

Right now, you should see the test running on your console with an error saying Error: no test specified; this is OK since we haven’t build any test yet.

Next, to make Enzyme knows how to compile our React application — we will need to create a small configuration to be run before the test; create a file insrc/setupTests.js with

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

and configure Jest to pick up the configuration when running the test by add this to our package.json :

"jest": {
"setupFiles": ["./src/setupTests.js"]
}

Now we can start building our tests.

Presentation Layer

In this piece, we will only test whether or not the component retains the information that we passed through it and if it also renders the way it should be. It will create a snapshot of the rendered element, so that if any changes are done to the component, we can catch it in our test.

Let’s create a component that we can create multiple test scenario

import React, { Component } from "react";class Home extends Component {
render() {
return (
<div id="main_wrapper">
<h1>Hello World</h1>
{this.props.text &&
<p>{this.props.text}</p>
}
</div>
);
}
}
export default Home;

with the component above, it has multiple possibilities of how it will be rendered. So let’s create the test that can do exactly that

import React from 'react';
import Home from './Home.jsx';
import renderer from 'react-test-renderer';
import { shallow } from 'enzyme';
it('renders Home container correctly', () => {
const tree = renderer
.create(
<Home />
)
.toJSON();
expect(tree).toMatchSnapshot();
});
it('renders Home with <p> for content', () => {
const tree = renderer
.create(
<Home
text="Nice to meet you"
/>
).toJSON();
expect(tree).toMatchSnapshot();
});

now we can run the test by running

npm run test

You will see that your test will succeed and a few files are created under the __snapshots__ folder; this will be the baseline of how your component should be rendered moving forward. Try modifying your component with an extra element or text and watch your test fail! (which is a good sign)

Don’t worry, the snapshots are not set in stone. If any intentional changes to how the component is going to be rendered, we can overwrite it for the next subsequent tests using

npm run test -- -u

Business Logic Layer

Now that we know how the component will render, let’s make sure the functions you added to the component can actually be triggered when we said it will. We are going to modify the same component

class Home extends Component {
constructor(props) {
super(props);
this._increaseCount = this._increaseCount.bind(this); this.state = {
count: 0
};
}
_increaseCount() {
const increase = this.state.count + 1;
this.setState({count: increase});
}
render() {
return (
<div id="main_wrapper">
<h1>Hello World</h1>
{this.props.text &&
<p>{this.props.text}</p>
}
{this.props.function &&
<div className="test_enter" onMouseEnter={this.props.function} />
}
<p>Current Count {this.state.count}</p>
<button type="button" onClick={this._increaseCount}>Click Me!</button>
</div>
);
}
}

And now we can create the test

it('should increase the state count by one', () => {
const wrapper = shallow(<Home />);
// verify initial state
expect(wrapper.state("count")).toEqual(0);
// "click" it once and see if value increase
wrapper.find('button').simulate('click');
expect(wrapper.state("count")).toEqual(1);
});
it("should trigger hover function on hover", () => {
const hoverFunction = jest.fn(), // Mock function that we can trigger
wrapper = shallow(<Home function={hoverFunction}/>);
wrapper.find('div.test_enter').simulate('mouseenter');
// verify that the mocked function has been triggered
expect(hoverFunction).toHaveBeenCalled();

Notice that we can trigger the test based on an action applied to the DOM or directly on the function inside the class. Use this to your advantage when testing out your component!

Tips on building a healthy Unit Test

There are multiple things that you can do to create a consistent unit test:

  • Utilize BeforeEach() and AfterEach() to create and reset your “playground” for each and every test.
  • Store your expected result as a variable to keep results consistent
  • When building, think how you plan on testing this. If a function get’s too complicated to run a simple test, then you should split it into multiple functions so you can test them individually.

Where to go from here

I wrote this as part of the series I have to create a full React web application that we will eventually deploy. I created repo for each section that I have written, so feel free to grab the sample from Github.

Check out my past topics:

Stay tuned if you want to know how to:

Digitizing Indonesias healthcare one microservice at a time. Currently engineering my life with real life lessons.