How to (unit) test in React

Onoufrios Malikkides
ITNEXT
Published in
9 min readJan 23, 2020

--

This guide explains what unit testing is, why it’s needed and the best practices for small to large React applications.

What is unit testing

Unit testing is a level of software testing where individual units/components of a software are tested. In the React world this means testing an individual React Component or pure functions.

Why should I test

Testing is essential to guarantee a stable application. Unit testing in particular is possibly the most important part of testing. The general consensus is that 70% of all our tests should be unit tests and for very good reasons.

  1. They are very fast to run allowing us to very quickly understand if there is anything broken in our app.
  2. They bring great value with the least amount of effort as they are very easy to write compared to other more complicated tests.
  3. If they fail it is very easy to know where the error is because they only concentrate on small units of code.

Note: Functional, integration or end to end testing is used for testing multiple units /components at a time. They are usually slow and harder to identify where the problem is.

Prerequisites for React unit testing

In order to do our unit testing we will use two very popular libraries namely jest and enzyme. Jest is a library written by facebook and has become very popular in the past few years (You could also use other libraries such as mocha or chai). Jest will help us to do all our assertions and enzyme will help us render React components in testing mode. You will see both in action in the following sections.

Our very first test

Our first test will be very simple . We will test a component whose only purpose is to render a paragraph with some text.

The only thing we want to test here is that the children passed to this component are rendered inside a <p> tag. Let’s go and write a test for that.

This is what happens in the test above:

a. First, in line 10 we configure enzyme for react 16 as per the documentation. This is something that we have to do in every test using enzyme so later we will add this in a helper function.

b. We use enzyme’s shallow renderer to render the Paragraph component. The shallow renderer is perfect for unit test as it doesn’t render any child components allowing us to focus testing in one component (This will be made clear in the following tests). Then our wrapper constant will contain all the nodes of this component.

c. Using enzyme’s find function we are looking for a <p> tag (Line 15) and then we assert that there was only one found (Line 16), which is what we expect from the Paragraph component to do.

d. Finally (Line 17), using the text function of enzyme, which returns a string of the rendered text, we assert that the text inside the <p> tag is the same as the children passed.

As you can see in only a few lines of code we tested a simple component and made our code more robust.

Testing a slightly more complex component

In our second test we will test a slightly more complex component than the simple component above. This time we will test a List component.

This List component above takes an array of items and renders them. If the array is empty an empty list message is rendered. The test for the component is as follows:

Note: that we have moved the enzyme setup in a helper function which we import and call before any test.

This is what happens in the test above:

a. In the first test (Line 13) we render the List component with an empty array of items. Then we assert (Line 16) that in this case a Paragraph component is rendered with its children being equal to the empty list message.

b. In the second test (Line 19) we render the List component with an array of 3 items. Then we find all those items (Line 23) using a css selector. We assert that 3 items (length of array) were found (Line 24). Finally we test that the text of the first item is ‘Shopping’ i.e. the item’s body.

It’s important to notice that in the above test we test in two different cases the text rendered inside a component (Line 16 and Line 25). In the first case we use .props().children and in the second case we use .text(). This is because the text function can only be used on an html element (i.e. div , p ) and not on React Component such as Paragraph. Also, notice that because we are using shallow the rendering stops at the Paragraph component and doesn’t render the implementation of Paragraph i.e. we never see the <p> tag in our tree. We only see the following:

<Paragraph>
This list is empty
</Paragraph>

Pro tip: The code above is generated by running console.log(wrapper.debug())

Testing a component involving state

Class Component

Component
Test

Note there are two sets of tests we can do for a class component.

  1. Behavioural. We can directly simulate a click on the button and see if the text of the button has changed. We are simulating the behaviour of a user and the testing the expected change.
  2. Component based. Test directly the toggle method of the component and see if it does what we expect it to do. This can be helpful when the toggle function is more complicated and we want to test different scenarios.

A combination of the two gives us a higher degree of confidence.

Update: The behavioural test (1) above is in theory an integration test, as it actually tests the toggle method indirectly.

For a true and complete unit test we would mock the toggle function and just test that the button calls that function, without caring about what happens next. Also, we would set the component in the desired state and check the render method against this. This is how a complete unit test would look:

Pure unit test

If the functionality of the class methods are quite complex (lots of branches, if/else e.t.c.) it is better to include all the unit tests. If the functionality of the class methods are not very complex (like the toggle above) the original test above should suffice.

Component with Hooks

Component
Test

Following the same pattern as the behavioural test of the class component above we can test our hook component.

Note: The toggle functionality could be extracted into its own hook to make it reusable.

Testing components with Higher Order Components

Injectors (Render prop)

Enzyme has a function to help us test render props quickly. Consider the following component.

In the example above, ModalHOC gives the children a toggleModal prop to trigger toggling a modal. To test the above component we would need something like this:

Note how we are using renderProp in line 20 to get a new wrapper of the children. This allows us to easily do our assertions using the children wrapper which has all the methods of the original wrapper (produced by using shallow).

Also we need to mock toggleModal to pass it as argument to our children prop. Here we are using jest function mock using jest.fn() .

Enhancers

Consider an example with the react router withRouter enhancer HOC.

Component

This is how we would test this:

Test

Note that we don’t have to worry about line 8. We can safely assume that withRouter is going to do its job and pass the correct props to our UserLink component.

So we test directly the UserLink component. Here we are mocking the history prop that is passed from the withRouter HOC and test that when the button is clicked the history.push function is called with the argument /users.

Tip: We can call jest.clearAllMocks() before each test. This is not very useful in this test because we only call history.push once. But if we where to test it multiple times it’s good practice to clear mocks before every test so that we start with a clean mock.

Learn to not worry about the rest of the system

As we saw from the previous test (Under enhancers) we don’t have to worry about the interaction of two component or one component and an enhancer HOC. This is the job of an integration test. That’s why we are using theshallow method and not themount method of enzyme.

A unit test should only concentrate on a particular piece of code. We are only testing that we get the desired output when passing certain inputs.

Coverage

You should be aware how many files/lines/branches e.t.c you have tested and jest makes this easy by running your tests with the --coverage flag. This will give you a report with a percentage of code coverage. Ideally you should be close to 100% code coverage. Realistically this could be somewhere in the region 80%–90%. From my experience a code coverage over 80% will give you lots of confidence that your code is not going to break. Although these might seem like big numbers, especially if you don’t have a lot of unit tests in place at present, if you make a habit of writing tests for all your new components you will be able to gradually approach this level of code coverage.

Note: Just because a component has 100% code coverage doesn’t mean it’s bulletproof. You should test a range of different (strange) inputs to increase confidence. Also, a 100% code coverage in your entire codebase doesn’t guarantee a bug free experience. As stated in the beginning of the article we need integration tests and e2e tests to make sure our units work as expected when they interact with each other.

A word on Test Driven Development (TDD)

TDD is essential for reaching code coverage close to 100%. TDD essentially tells us to write a test for every piece of code we want to write. A lot of the articles suggest this process:

1. Add a test

2. Run all tests and see if the new test fails

3. Write the code (Doesn’t have to be perfect, just enough to make test pass)

4. Run test (Make sure it’s green)

5. Refactor code (To make code more perfect)

I personally prefer writing the component first, as it allows for more creativity, then write my tests and then refactor. No matter which approach you take the important thing is to try and test all scenarios (edge cases included) for a certain component. And also make the test fail at least once so that you are sure your test is meaningful.

Additional Tips

  1. It helps to have a fixtures folder where you keep constants with data coming from the api. For example you could have a userWithPosts fixture which is a user with a certain amount of posts and also a userWithoutPosts fixture which is a user without any posts. This will allow you to quickly recall these constants to use them in your tests. Furthermore if the structure of the api response changes you can change the fixture in a single place and see if any tests fail.
  2. You should write your own helper functions that will make testing easier when parts of the test you write are repeatable. For example you could write a function named testRenderComponent which takes as arguments a wrapper, an element e.g. button and a number e.g. 2. This would test that there are 2 buttons rendered inside the wrapper.

I am still confused about what I should test

If you are still unsure about what you should test, try this.

  • Test what you expect the component to render.
  • Test all different states of this component (branches). If you expect different things to render depending on the props passed, shallow render the component with different set of props and make the appropriate assertions.
  • Test actions. For example if you expect something to happen when you click or hover over a button test that.
  • Test the props passed to a sub component. If you are rendering a custom Button component and you pass a color prop with a value of red you should test that. This will also give you confidence that your component meet the design requirements.

Bottom line: If you or another developer change the component in a way that it changes its behaviour at least one test should fail.

Conclusion

It’s important in every project to write unit tests, which are the easiest kind of tests, in order to increase confidence in the correctness of the code we write. We should aim for a high percentage of code coverage and make it part of our workflow to write unit tests.

Happy testing!

--

--