Unit Testing with Jest and Enzyme

We saw the Jest test runner in Project Setup. We modified an existing test but didn’t dive into testing.

In this tutorial step we start the process of test driven development. After this step, we’ll develop first in our Jest tests. Then, only at the end, we will look at the app in the browser.

Pretty Jest

Previously we ran our Jest tests as a generic npm run script, in the IDE’s npm run tool window. However, WebStorm has a dedicated run configuration type for Jest. It’s a fantastic way to do development. Let’s switch to using that.

Select Run | Edit Configurations, click +, and click on Jest. Supply a Name: of something like unit tests. The only real field you need to supply is Jest options:. For that, enter --env=jsdom --watchAll. This tells Jest to re-run tests when files change and to use the jsdom package as a fake browser.

Custom run configuration type for Jest

Save that run configuration and run it. Our tests now run in a nice tool window which will make test-driven development (TDD) much more productive.

Fail Faster

Let’s see a little testing in action. Open src/App.test.tsx. We’re going to show the cycle of fail-fix in action. Define two contants, then compare them with a simple Jest (actually, Jasmine) assertion:

it('renders without crashing', () => {
    const div = document.createElement('div');
    ReactDOM.render(<App/>, div);
    ReactDOM.unmountComponentAtNode(div);
    const actual = 1;
    const expected = 2;
    expect(actual).toBe(expected);
});

When you save this, Jest re-runs your tests, and does so quite fast. Our tests fail, and the IDE’s tool window presents the test results in a very convenient manner. For example, you can jump directly to the line of the failing test.

Jest tool window shows which tests fail

Fix the test by changing expected to 1 then save. The Jest watcher spots the change, re-runs the test very quickly, and shows that all tests pass.

TDD Basics

JavaScript development is usually a bunch of switching between the editor, the browser, the browser console, and a terminal window with the build tools displaying messages. Let’s use a better flow. Let’s stay in the IDE and focus on our code, and observe our code through tests instead of a browser reload.

First, let’s get our code and our tests side-by-side. Press Ctrl-Alt-A and type in Split Vertically. This gives us a left and right side editor. On the left, open App.tsx. We can now see class App alongside our tests. If you need more room, close the Project tool window.

Component and test side-by-side

A Real Test

We currently have a test which makes a document, tells React to render our component-under-test into it, and then…well, nothing really. create-react-app generates a test whose only purpose is to see if it can render. Let’s look inside the rendered result and test its correctness.

To do so, we’re going to install Enzyme, a utility for React that makes testing feel like jQuery assertions. Open the IDE’s Terminal tool and install Enzyme and its TypeScript typings:

$ npm install -D enzyme enzyme-adapter-react-16 react-addons-test-utils \
  @types/enzyme @types/enzyme-adapter-react-16

We need to tell Jest to use a configured Enzyme. Add this file at src/setupTests.ts:

import * as Enzyme from 'enzyme';
import * as Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({
    adapter: new Adapter(),
});

Restart the Jest run tool window to pickup this setup file. Then, edit src/App.test.tsx to include a second test:

it('renders the heading', () => {
    const wrapper = shallow(<App/>);
    expect(wrapper.find('h1').text()).toBe('Hello React');
});

You’ll see shallow in red, meaning it is a TypeScript error, because shallow hasn’t been imported. Click on shallow and press Alt-Enter. The IDE automatically generates the correct import.

You can now test the TDD style of development. Try changing the component’s <h1> to contain different text and save. You’ll see the test fail. Change it back and save, and the tests pass.

To see real TDD, you write the test first. Add a third test in src/App.test.tsx:

it('renders the paragraph', () => {
    const wrapper = shallow(<App/>);
    expect(wrapper.find('p').text()).toBe('Nice TDD');
});

Good news, it fails! TDD starts with a failing test. You then implement what you expect to pass. Change your App component in src/App.tsx to have this markup:

<div>
    <h1>Hello React</h1>
    <p>Nice TDD</p>
</div>

When you save, the test passes. Not only that…you extended your component without looking at a browser.