Automated JavaScript Tests with React, Jest and Enzyme

Adam Hewitt
Glasswall Engineering
4 min readDec 2, 2019

The response to React as a front-end framework within Glasswall has been incredibly positive, our cloud team has adopted it and started developing some amazing apps. Our plan now, is to incrementally convert the front-end of our Portal into testable components.

The major factor in the push for React was the ability to write automated tests on our front-end, something we lacked up until this point in our Portal app (written entirely in JQuery and Handlebars.js).

Test Pyramid

We’re going to follow the concept of the Testing Pyramid.

End-to-End tests represented by our Snapshot tests (i.e testing that the end user sees what we expect them to see).

Integration testing with Enzyme to make sure our components play nicely with each-other.

Unit tests are achieved by shallow-rendering components in isolation.

Unit Tests With Shallow Rendering

Enzyme is a testing utility maintained by Airbnb, it allows us to test the output of our components, and even simulate runtime. One big advantage is the ability to manipulate and loop through the DOM.

Shallow rendering is a feature that allows us to isolate a single component without rendering any child components, this is great for writing simple and quick unit tests.

UsersTab Component

In this shortened version of our UsersTab component, we wait for the domIsUpdated property in state to be set to true before displaying our ReactFragment. While it’s false, we render the DataSpinner component.

Our componentDidUpdate method keeps calling refresh (which sends an AJAX call to get a list of users) until it sets domIsUpdated to true, so we’re never left hanging with a data spinner.

Unit Test

The test is making sure we see our loading spinner while there’s no user data. We run a shallow render on the UsersTab component, then we use Enzyme’s find function to make sure we can see the loading spinner’s container div.

Integration Tests With Enzyme’s Mount

Mount allows us to simulate mounting the component to the DOM, which will fire the componentDidUpdate, shouldComponentUpdate and componentDidMount methods. It will also attempt to mount any child components.

Returning to our UsersTab example, we’re rendering a UsersTable.Table component once domIsUpdated is true, we’re passing the data to the table using props (this.state.Users and this.state.AvailableGroups). We’re also passing a formId to the table.

Integration Test

In the test, we create our test wrapper with the mount function. Because the component is now mounted to the DOM, we can set the state, which will trigger an update. Once the wrapper is updated, we check the DOM using the find function, and we verify the UsersTable has been rendered with the correct Id, which happens to be the one we passed in as a prop.

We run a cleanup at the end to prevent clashes with any other tests in the same test suite.

Snapshot Tests

Snapshot tests are useful for ensuring the actual output of your UI matches your expectations.

It works by building your component, rendering the DOM and saving the result as a string in a .snap file. Then in our tests, we assert by comparing the .snap to a newly-rendered/updated version of the DOM, held as JSON.

Here we have a Data Spinner component, it simply renders a div on the screen, with a ‘Loading Data’ message. In our other components, we’re displaying this spinner while waiting for data to be returned from an AJAX call.

Snapshot Test
.snap Result

This is the snapshot test (we’re using react-test-renderer) and the generated .snap file. Jest will display the snapshot in a human-readable format so you can visually identify any problems with output.

When you run the test, the previous snapshot is compared to the newly created DOM, and if there are any changes, the test fails. This is quite useful for tracking unforeseen changes in your UI when you upate your components.

--

--

Adam Hewitt
Glasswall Engineering

Dev at Glasswall-Engineering, with a few years .NET stack experience and a passion for client-side development.