DEV Community

Cover image for Testing React: A convert’s journey from Enzyme to Testing Library
Bonnie Schulkin
Bonnie Schulkin

Posted on

Testing React: A convert’s journey from Enzyme to Testing Library

Image by Shri ram from Pixabay

I’m a big tester. Testing has saved my bacon enough times that I feel uneasy coding without it, like rock climbing without a rope. Even if I’m in a gym and the floor is really springy, a rope (like testing) helps minimize the damage when things inevitably go wrong.

When I started learning React several years ago, Enzyme was all the rage for test rendering and DOM manipulation. I come from an old-school back-end testing background (meticulous unit testing and isolation). Enzyme seemed great! I could take the same testing approach with React, testing state and other implementation details to my heart’s content.

Then I started hearing about Testing Library (specifically testing-library/react). At first I was resistant: an opinionated library which enforced someone else’s ideas of “best practices”? My detailed unit testing approach was under threat! I steered clear.

As time went by, it became clear Testing Library was here to stay, so I thought I’d check it out. After trying it for one project (my personal website), I am a total convert. Read on for my reasons why.

Opinionated === Best Practices

Testing Library encourages your tests to interact with your code the same way your users would. For example: Find an element and click it. Then, instead of checking to see whether the state changed (sorry, not an easy option in Testing Library!), check to see whether the page changed in the way you expect.

This is known as testing behavior (what’s on the page) vs. testing implementation (internal state). After all, user experience determines whether your code is working according to spec — not what state happens to be.

The downside: when tests fail, there’s less granularity pointing to where exactly the tests failed (is the state the problem? Or is it something else in the onClick callback?). The upside: less test refactoring when your code changes. It’s a trade-off, but in front-end testing, I’m coming around to the “fewer, more robust tests” philosophy. The time spent tracking down errors is less significant than the time spent maintaining tests.

I’ve especially come to appreciate this in the past year or so as React has moved from class-based components to functional components with hooks. If the behavior doesn’t change from the user perspective, there should be little-to-no cause to update tests when updating the implementation. Trends in React will continue to evolve, and your app can evolve with them without requiring a major testing overhaul.

Accessible Code

Another opinion from Testing Library: how to find page elements in your tests. The recommended element specifier is role, which relies on the accessibility tree (aka how elements appear to screen readers and other accessibility tools). Using role to find page elements for tests makes sure you have accessibility at top-of-mind while creating your content.

You can also use Enzyme to find elements by role, but it is not nearly as easy: 1. You have to use the generic find method to find role attributes

  1. You have to account for cases for all attributes that represent role (for example, aria-role and role)

  2. You have to account for inferred roles (like the “button” role for a element).

In Testing Library, you get all these role indicators for free with the getByRole method.

jest-dom Assertions

Jest is pretty generic when it comes to assertions: You can test things like “does this variable have the right value?”, or “does this object have the expected property?”, or “was an error thrown?”, but there’s nothing specific to the DOM.

Testing Library’s jest-dom to the rescue! Let’s say you want to know whether an element is visible on the page. You could use Enzyme to see whether the element has the proper class, and use Jest’s toBe, like so:

expect(myElement.toHaveClass('hiddenClass')).toBe(true);
Enter fullscreen mode Exit fullscreen mode

Or, you could use jest-dom’s [toBeVisible](https://github.com/testing-library/jest-dom#tobevisible) :

expect(myElement).not.toBeVisible();
Enter fullscreen mode Exit fullscreen mode

By testing what the user sees (whether the element is visible, rather than whether it has a certain class), you’re testing behavior, you savvy tester, you.

There are also lots of other useful DOM specific assertions, for example: [toHaveFocus](https://github.com/testing-library/jest-dom#tohavefocus) and [toBeChecked](https://github.com/testing-library/jest-dom#tobechecked) . This makes for robust, concise, and extremely readable test code.

Since the jest-dom assertions do not work with Enzyme wrapper objects, they are not available to use in Enzyme (at least not in any way I could figure out). Another item in the “plus” column for Testing Library.

ESLint Plugins

To go even further in encouraging best practices, both testing-library and jest-dom have ESLint plugins to give feedback when you’ve gone astray.

For example, say you want to test that a checkbox is checked by default. You think to yourself, [toHaveAttribute](https://github.com/testing-library/jest-dom#tohaveattribute) is the way to go! So you type out your test:

jest-dom eslint hint for toHaveAttribute(‘checked’)

Wait a minute… what’s that angry red line under toHaveAttribute? A mouseover reveals that [toBeChecked](https://github.com/testing-library/jest-dom#tobechecked) is preferred here. Nice way to be reminded of best practices (and even possibly learn a new assertion).

Helpful Hints on Roles

Sometimes it’s hard to know which role to specify when searching for elements. Never fear — if you search for an element by role and Testing Library can’t find it, the test output tries to steer you right. For example:

Test output shows “link” role instead of “heading” role for $12.99 text

Ooh! So I guess that element has a role of ‘link’ and not a role of ‘heading.’ Thanks, Testing Library. 😁

Conclusion

I tinkered with Testing Library on a small project, and I can see why it’s gained so much momentum. The opinionated library encourages best practices such as testing behavior over implementation, and writing accessible code. Its jest-dom assertion library provides simple, readable tests. Finally, the ESLint plugins and test output help with best practices and finding elements. I am a big fan!

photo of large fan

Photo by Enrique Zafra from Pexels

Top comments (2)

Collapse
 
fires3as0n profile image
fires3as0n

I am coming back to React after 2.5 years of working with other libraries, and when the time came for unit tests, I was surprised that enzyme was losing its popularity in favor of another library - I was in doubt about what to use. 

Then I remembered about a perfect testing course on Udemy I used in 2019. I thought that a person who made it should definitely have a good idea of what to use in 2022. And you do! Thank you a lot! (again :))

Collapse
 
srshifu profile image
Ildar Sharafeev

If you are looking for a tool to automate monitoring of Enzyme deprecation, you can find link here: thesametech.com/migrate-away-from-...