JavaScript Testing

Introducing Enzyme and testing React components

Marcin Wanago
JavaScriptTesting

At the previous part of the tutorial, we’ve briefly covered the basics of unit testing. This time we will go further and start testing React with the Enzyme library. Thanks to the that, your application will be more reliable and it will be easier to avoid regression. Even though we will be using Jest here, Enzyme also works with libraries like Mocha and Chai.

JavaScript testing tutorial: the basics of Enzyme

Enzyme is a library for manipulating your React components while testing. It was developed by the company Airbnb.

Setting up Enzyme

I will go ahead and assume that you have Jest already working. If not, feel free to take a look at the previous part of the course. Let’s start with installing Enzyme

1npm install enzyme

The first thing you will need to do is to create a  setupTests.js file. It will contain the usage of an adapter which is an additional library that allows you to use Enzyme with a specific set of React versions. I will use React 16.x, therefore I will install enzyme-adapter-react-16. For a compatibility table check out the Enzyme repo.

You can also find adapters to libraries like preact and inferno
1npm install enzyme-adapter-react-16

Once you have this sorted out, the content of your setupTests.js  file should look like that:

setupTests.js
1import { configure } from 'enzyme';
2import Adapter from 'enzyme-adapter-react-16';
3 
4configure({adapter: new Adapter()});

The last thing to do is to provide a path to this file in the package.json

package.json
1"jest": {
2    "setupTestFrameworkScriptFile": "<rootDir>/app/setupTests.js"
3}

And there you go, you’re all set!

Shallow rendering

The most basic usage of the Enzyme library is the shallow rendering. It allows you to render only the parent component without its children. It makes the shallow rendering not only fast but also great for unit testing. This way you won’t be testing any other components that the one that you pass to the shallow function.

App.js
1import React from 'react';
2 
3const App = () => {
4  return <h1>Hello world!</h1>
5}
6 
7export default App;
App.test.js
1import React from 'react';
2import { shallow } from 'enzyme';
3import App from './App';
4 
5describe('app component', () => {
6  it('contains a header with the "Hello world!"', () => {
7    const app = shallow(<App/>);
8    expect(app.containsMatchingElement(<h1>Hello world!</h1>)).toEqual(true);
9  });
10});

In our simple test, we are checking if the App component contains a certain header. After running  npm run test you will see a success message.

1PASS  app/App.test.js
2  app component
3    ✓ contains a header with the "Hello world!"

A very important thing to remember here is that even though we are using Enzyme, our test runner is still Jest. Since we are using the expect function, you have access to a wide variety of matcher functions, that you can call. We’ve mentioned them in the first part of the course. For a list, visit the Jest documentation.

Let’s create a bit more interesting tests. To do that, we will create a brand new component.

ToDoList.js
1import React from 'react';
2 
3const ToDoList = (props) => {
4  return (
5    <ul>
6      {
7        props.tasks.map((taskName, index) =>
8          <li key={index}>{taskName}</li>
9        )
10      }
11    </ul>
12  )
13};
14 
15export default ToDoList;

Let’s test what happens if the provided list of tasks is empty, and what happens if it contains tasks.

ToDoList.test.js
1import React from 'react';
2import { shallow } from 'enzyme';
3import ToDoList from './ToDoList';
4 
5describe('ToDoList component', () => {
6  describe('when provided with an empty array of tasks', () => {
7    it('contains an empty <ul> element', () => {
8      const toDoList = shallow(<ToDoList tasks={[]}/>);
9      expect(toDoList).toContainReact(<ul/>);
10    })
11    it('does not contain any <li> elements', () => {
12      const toDoList = shallow(<ToDoList tasks={[]}/>);
13      expect(toDoList.find('li').length).toEqual(0);
14    })
15  });
16 
17  describe('when provided with an array of tasks', () => {
18    it('contains a matching number of <li> elements', () => {
19      const tasks = ['Wash the dishes', 'Make the bed'];
20      const toDoList = shallow(<ToDoList tasks={tasks}/>);
21      expect(toDoList.find('li').length).toEqual(tasks.length);
22    })
23  });
24});

The tests pass without problems, but we should explain a few things introduced here.

In the first test, we’ve used a  toContainReact function, which is a custom matcher function. It is a part of the enzyme-matchers library. To use it with Jest, install jest-enzyme package.

1npm install jest-enzyme

The last thing to do is to import it in our setupTests file.

setupTests.js
1import { configure } from 'enzyme';
2import Adapter from 'enzyme-adapter-react-16';
3import 'jest-enzyme';
4 
5configure({adapter: new Adapter()});

For a list of function it provides, check out the readme. You might find it useful.

In the second test, we’ve called a  find function on our component. This is thanks to the fact that the  shallow function returns the ShallowWrapper, which is a wrapper around the rendered output. It has a set of functions that you can call on it. To check the list go to the Enzyme documentation.

Running all our tests gives us a success message!

1PASS  app/App.test.js
2PASS  app/components/ToDoList/ToDoList.test.js
3 
4Test Suites: 2 passed, 2 total
5Tests:       4 passed, 4 total
6Snapshots:   0 total
7Time:        1.41s
8Ran all test suites.

Summary

Today we’ve learned the very basics of testing React components with Enzyme. We’ve covered installing Enzyme and running our first, simple tests. The type of rendering that we’ve used is called shallow rendering. It is called like that because it does not render any child components. It works very well when writing unit tests. In the upcoming parts of the tutorial, we will cover other types of rendering and learn how to test many different parts of our application. It will include techniques like snapshot testing and mocking data. See you next time!