Testing in React with Jest and Enzyme: An Introduction

An introduction to testing methods in React and the tools needed for the job

Start integrating Tests within your React Apps

As React apps grow in complexity there becomes a higher likelihood that code will break. Whether abstracting, adding features or refactoring, there always lies the possibility of bugs being introduced — and this is multiplied as more developers start contributing to the project. The solution we need here are automated tests that can be run in real-time during development.

This article will introduce the tools needed to do this, what types of tests we can carry out in React, and why we would want to do so. Let’s start the journey of bug free React apps by introducing Jest — the most widely adopted Javascript testing package that Facebook have opted to use as their primary means of testing React apps.

Introducing Jest

The first tool we will visit is Jest. Jest is a node-based test runner allowing fast parallel running of tests in a node environment. It works out of the box within any Create React App project. Running npm test in a Terminal window within your app directory will initialise Jest and start testing in watch mode — meaning changes to files will re-run the tests associated with those files immediately as you are developing.

But, what about Mocha?

You may have read about or even used another Javascript testing framework called Mocha; an alternative capable and feature rich package for asynchronous Javascript testing. Even though this article is Jest focused primarily, it is worth noting that Mocha is another suitable testing framework that also has strong adoption amongst the Javascript community.

At the time of writing Jest on NPM is boasting 3,695,443 weekly downloads, showing a lot more momentum in adoption than the Mocha package, currently on 2,247,393 weekly downloads. Both packages have been around a while — Jest on 147 releases and Mocha on 146 releases — and their histories have had highs and lows, prompting many online articles to route for one package or the other. However, it is clear in 2019 that Jest is winning the battle as the dominant React testing suite.


Not using Create React App?

If you are not using Create React App, you can install Jest with npm or yarn:

yarn add --dev jest
#or
npm install --save-dev jest

In order to run jest with npm test, replace or append atest script to your scripts block within your package.json file:

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

Within a Create React App environment this will look slightly different, as react-scripts is called instead of jest: (Nothing needs to be changed here)

...
scripts: {
"test": "react-scripts test",
...
}

To verify things are working, run npm test now in your Terminal at your project root directory. You will now be in watch mode, and should see a prompt displaying something similar to “No tests found related to files changed since last commit”, along with some shortcuts to run tests in various ways.

So what is the best way to introduce tests to your React project? Let’s visit this first before diving into some more useful tools.

Start Using Jest

Jest discovers test files within your project via their filenames, of which can be located at any depth of your project. There are 3 naming conventions we can adopt in order for Jest to pick up our tests:

  • Any file with a .test.js suffix or a .spec.js suffix. This may be preferable when a component (e.g. App.js) can be accompanied by an App.test.js file in the same directory, where they will reside next to each other. This optimises discoverability and keeps import statements to a minimum.
  • Any .js file within __tests__ folders throughout your project. If you have multiple test files to test a particular component or component directory, a __tests__ folder allows a more coherent structure whereby your tests and components are not mixed. This may be preferable in larger projects.

Which method you adopt is your call and will depend on your project.

Now let’s write a simple test to examine Jest syntax. Let’s add an App.test.js file within the same directory as App.js. This test will have no relation to App.js just yet, but instead will introduce some key methods of Jest; describe() and it(), as well as the expect() methods:

App.test.js:

describe('Examining the syntax of Jest tests', () => {

it('sums numbers', () => {
expect(1 + 2).toEqual(3);
expect(2 + 2).toEqual(4);
});
});

Upon first glance you may find this test quite obvious, and that is a good thing; readability is an intentional design choice to keep tests simple and easy to understand.

Now, upon saving this test and provided you have Jest running in the Terminal, you should receive an output similar to the following:

Let’s break down the above example to understand this syntax:

  • describe(): An optional method to wrap a group of tests with. describe() allows us to write some text that explains the nature of the group of tests conducted within it. As you can see in the Terminal, the describe() text acts as a header before the test results are shown.
  • it(): Similar in nature to describe(), it() allows us to write some text describing what a test should successfully achieve. You may see that the test() method is used instead of it() throughout the Jest documentation, and vice-versa in the Create React App documentation. Both are valid methods.
  • expect() and .toEqual(): Here we carry out the test itself. The expect() method carries a result of a function, and toEqual(), in this case, carries a value that expect() should match.

expect() makes assertions: what is an assertion?

expect() is a global Jest function for making assertions. An assertion takes a boolean valued function and always expects that return value to be true — hence the name expect. In the event that false is returned, the test fails and execution stops within the corresponding it() or test() block.

toEqual() is a matcher: what is a matcher?

toEqual() is called a matcher. A matcher is a function whose resulting value must be true relative to what it is testing from expect(). Jest have documented the full list of matchers here, ranging from toContain, toBeFalsy, toMatch(regexpOrString) and toThrow(error). It’s worthwhile familiarising yourself with what is available to optimise your test syntax.

We get a full breakdown of each test result in the Terminal. Green ticks highlight a success whereas red crosses highlight a failure.

To highlight a failure (hopefully a rare occasion!) let’s change the last test to expect(2 + 2).toEqual(5):

Our terminal now displays some handy output, including exactly where our test failed within the script itself, line number and code.

Why test two or more sums instead of just one?

Our first example above tested a sum function twice, the first time with 1 + 2 and then with 2 + 2. This is important; and to demonstrate why, let’s now introduce a sum() function instead of hard-coding our sums:

./math.js:

export const sum = (x, y) => x + y;

./App.test.js:

import {sum} from './math';
describe('Examining the syntax of Jest tests', () => {

it('sums numbers', () => {
expect(sum(1, 2)).toEqual(3);
expect(sum(2, 2)).toEqual(4);
});
});

Making these changes will again yield successful tests. However, what if we change sum() to the following:

export const sum = (x, y) => 3;

What we have done here is simply return a hard-coded value, 3, from our sum function. Now, if we simply left our test suite with a singular assertion of a 1 + 2 sum, the tests would have passed even though the sum function itself does not function as we wish — it always returns 3!

Why would this happen? Mistakes are made during development. I may wish to test a fetch request handler with hard-coded values, and later on forget to switch back to my dynamic call. I could even be coding a multitude of database calls and forget to plug in some queries from my placeholders. In any case, this demonstrates that it is always useful to carry out multiple assertions for each function or component within your it() or test() block.


Up until this point we have been testing vanilla Javascript functions. Now let’s explore how we can test React components. To do so we will turn to another package to use in conjunction with Jest: Enzyme.

Testing React Components with Enzyme

Enzyme is a testing utility package developed by Airbnb that makes testing React components easier. React do provide their own testing suite, but these tools can be verbose and granular, whereas Enzyme provides more streamlined methods to carry out tests.

Unlike Jest, Enzyme is not included with Create React App. Install Enzyme with the following:

yarn add --dev enzyme enzyme-adapter-react-16 react-test-renderer
#or
npm install --save-dev enzyme enzyme-adapter-react-16 react-test-renderer

The package you install is dependant on the version of React you are using. React 16 is used here, but Enzyme provide other packages for previous versions also.

A src/setupTests.js file also needs to be present in order to use Enzyme, which essentially configures Enzyme to run with React using the adaptor we just installed. Make sure this file is included before using Enzyme:

src/setupTest.js:

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

We can go ahead and begin using Enzyme within our tests. For example, if we wish to test our <App /> component, we can extend our App.test.js file by adding the following:

import React from 'react';
import { shallow } from 'enzyme';
import App from './App';
describe('First React component test with Enzyme', () => {
it('renders without crashing', () => {
shallow(<App />);
});
});

Here we have used the shallow() assertion method. shallow() will test the component we are providing, and ignores any child components that may be present in the component tree thereafter; if we had a <Header /> and <Footer /> component within <App /> for example, they would be ignored in this test.

shallow() is a type of unit test for React. A unit test is a test specifically designed to only test one function. When we are testing functions that rely on other functions, we refer to such tests as integration tests. Likewise, if we are testing an entire React component tree instead, this would be an integration test.

In Enzyme we can do such a test with mount(); a full rendering test that takes the entire component tree and lifecycle methods into consideration.

How does Jest test within a DOM environment?

We know that Jest runs in a node environment, but React components need to be tested within a DOM, so how are they tested? By using a package named jsdom, a Javascript implementation of the WHATWG DOM and HTML Standards.

jsdom is enabled by default within our Create React App testing environment, and as such, globals including window and document are provided allowing us to mimic browser behaviour when running our tests. Although jsdom allows us to perform unit and integration tests for our components, it is not recommended to perform end-to-end browser tests: Run an actual browser driver to see real world results.

You can disable jsdom by passing the --env=node flag with your test command within package.json, but this will unlikely be suitable when integration testing React components as there will very likely be some element of markup being rendered.

Create React App documentation highlights the cases of when you can disable jsdom here. Disabling the package will simply make your tests run faster.

Testing Terminology

At this point it is worth going over the main terminology we encounter with React testing — let’s go over some commonly used terms:

  • Unit test: Testing one isolated function, or one React component. Enzyme’s shallow() is a unit test.
  • Integration test: Testing a multitude of functions working together, or an entire React component including children components. Enzyme’s mount() is an integration test.
  • Mock function: Redefining a function specifically for a test to generate a result. E.g. returning hard-coded data instead of relying on fetch requests or database calls. This strategy could prevent the hard-coded sum issue we were discussing earlier!
    Mock functions can be defined in jest with jest.fn(() => { //function here });.

Testing terminology:

  • Smoke test: Verifies that a component renders without throwing. Utilising Enzyme’s shallow() is indeed a smoke test, as is mount().
  • Shallow rendering: rendering a component with no children. Enzyme’s shallow() is a form of shallow rendering, whereas mount() is not.
  • Full rendering: Rendering a component and all of its children. mount() is a full rendering method whereas shallow() is not.
  • Static rendering: Rendering components to static HTML files and then analysing the results. Enzyme’s render() method is used for static rendering.

What Next?

We will wrap things up here to save this article from becoming too long. The Jest and Enzyme packages offer a lot of functionality, much more than we can fit within this introductory article.

If you would like to continue reading, my next article focuses on test driven development (TDD), as well as coverage of the most widely tools Jest and Enzyme have to offer, including snapshotting, mocking and simulating events:


Jest have a dedicated page for testing React apps, of which accompanies a range of guides on various test integrations. Enzyme too offers great documentation. The API reference homepage is split into the 3 rendering types (shallow, full and static). Start browsing here.

If this is your first endeavour into React testing, enjoy your first steps into making your React apps bug free.