How to use React Testing Library to rewrite an Enzyme Component test

A mini-rewrite case study of two testing libraries

Luke Ghenco
Jan 16, 2019 · 7 min read
The Dream of 100% Test Coverage

Learnings from a recent conversion

Since I’ve been learning a lot in this conversion process, I wanted to share some solutions so that you too might be able to convert tests around a React component from using Enzyme to using React Testing Library. I also wanted to share some cool bonuses you might get in the process of conversion (improved test coverage anyone?).

Quick Setup Info

The Component(s) we are testing

Step: 1

The original Enzyme test

Step: 2

So before we start refactoring and talking about changes, we should state some facts about our tests using the Jest coverage tool. Running yarn test — coverage (or npm run test — coverage, if you use NPM), I get the following info:

  1. 54 lines of code in test file.
  2. The code coverage for TextAreaInput.jsx is currently 90% on statements and 62.5% for branches.
  3. The code coverage for required InputError.jsx is currently 42.86% on statements and 0% for branches.
  4. The overall code coverage for both components is currently 72.2% on statements and 50% for branches.

{mount, shallow} vs render

The tests we have above only use Shallow, so each component is tested individually and does not signify that the component is working together as a whole in the UI.

The Render function we will be using from the React Testing Library works similarly to Mount and will render all of the child components for us as well as have access to lifecycle methods (our example code uses stateless components so this is a non-issue for the following tests) in its mount and un-mounting process.

Looking for similar test patterns

So for example, let’s refactor this portion:

Step: 3

With the following:

Step: 4

We can then continue the trend as we move through the code, so we will be able to go from our original test (see code in step 2) to our new tests using React Testing Library:

Step: 5

Just a couple of notes on this before we discuss the improvements. You will notice I used the getByLabelText() function to find a textarea tag. One of the cool things about React Testing Library is how it expects and pushes you to use best practices. An input should have a related label attached to it for accessibility reasons. To make this happen an id attribute on a textarea tag must match the htmlFor attribute on a label tag.

So what did we gain from the code above?

What stands out in my mind is more readable code, due to not having to test as many implementation details. We searched for error message text to be rendered, which verified that the InputError component was working correctly, versus only checking to see if the InputError component was mounted (i.e. const node = this.wrapper.find(InputError)). This allows us to have easier and better refactoring powers later, as it releases us from having to rely on the InputError component. As long as we successfully handle rendering the error message prop in some form or another the user experiences the same results.

This is a bit subjective, but I find that the description blocks can be rewritten to be more human readable. The description seems to map closer to the actual code in the specs.

Lastly, code coverage has improved. Let’s compare specs again using yarn test — coverage:

  1. 51 lines of code in tests. (could be less by just using getByText() to find element and not checking for it to be defined, as that is redundant)
  2. The code coverage for TextAreaInput.jsx is now at 90% (no difference) on statements coverage and 70% (+7.5% improvement) for branch coverage.
  3. The code coverage for required InputError.jsx is now at 100% (+57.14% improvement) on statements coverage and 100% (+100% improvement) for branch coverage.
  4. The overall code coverage for both components is now at 94.4% (+22.2% improvement) on statements coverage and 80% (+30% improvement) for branch coverage.

Looking for test paths that we didn’t cover in Enzyme

Branch paths we are not testing currently:

  1. What is the class name for the textarea tag if no className prop is present?
  2. How does it connect label tag to textarea tag if id prop is not present?
  3. How does it disable the textarea tag?
  4. How does it handle placeholder text?
  5. Will it render the value prop inside of the textarea tag?

There are probably more paths I’m missing, but this seems like a good start.

Let’s work on the checking for the default behavior of the class name attribute for the textarea tag. To test this, we should be able to add the following code:

Step: 6

Then to check how a textarea tag is connected to a label tag using the id and name props. We will add a test for two different branching paths:

  1. When an id prop is present:
Step: 7
  1. When an id prop is not present, but the required name prop is:
Step: 8

Checking for a disabled textarea tag will be pretty similar to the tests for connecting label tag to textarea tag. The two different branching paths will be:

  1. When disabled prop is present.
Step: 9
  1. When disabled prop is not present.
Step: 10

To check that it renders a placeholder we should be fine with just one test.

Step: 11

For the last test, we will make sure it renders the value prop. This will also add verification that an event handler is present on the textarea tag to make it dynamic instead of only being a read-only input.

Step: 12

Checking out the final code coverage using the new tests

Step: 13 — Final

We’ve increased the amount of lines of code from 54 -> 106, but when we run yarn test — coverage, we get a much happier score card:

100% on everything!

In retrospect, for a real world scenario, the InputError component is probably used by more than one parent component, so additional test coverage would be recommended. My goal here was to show how we could improve test coverage of two components at once by just testing the parent component. I believe that was successful.

Closing Thoughts

With that said, Enzyme is a great tool, and not every team will be able to switch over. For those teams, I would highly recommend implementing React Testing Library’s guiding principle “The more your tests resemble the way your software is used, the more confidence they can give you,” and only use Mount to verify that your components are working as a unit.

Thanks for reading! Want to work on a mission-driven team that loves React and high confidence testing? We’re hiring!

Footer top

To learn more about Flatiron School, visit the website, follow us on Facebook and Twitter, and visit us at upcoming events near you.

Flatiron School is a proud member of the WeWork family. Check out our sister technology blogs WeWork Technology and Making Meetup.

Footer bottom

Flatiron Labs

We're the technology team at The Flatiron School (a WeWork…

Flatiron Labs

We're the technology team at The Flatiron School (a WeWork company). Together, we're building a global campus for lifelong learners focused on positive impact.

Luke Ghenco

Written by

Flatiron Labs

We're the technology team at The Flatiron School (a WeWork company). Together, we're building a global campus for lifelong learners focused on positive impact.