react-testing-library vs. Enzyme - LogRocket Blog (2023)

Editor’s note: This article was last updated 23 March 2023 to reflect the most recent information for Enzyme and react-testing-library. Check out this guide to working with Enzyme for React for more information.

When we talk about testing React components, we might end up with a handful of options that you should consider according to how well they are used in the community and how easily you can get information on them through the documentation.

These options include tools like Enzyme, react-testing-library, or React Test Renderer, which have rich documentation and use cases available in the React community.

For this post, we’ll review two of those tools: Enzyme and react-testing-library. We’ll also discuss how to migrate from Enzyme to react-testing-library and how to migrate back.

We’ll cover the following:

  • Context for React components testing tools
  • Why do we need to test React components?
  • What is the react-testing-library?
  • What is the Enzyme testing library?
  • Example React components for testing
  • Testing with Enzyme vs. react-testing-library
  • Migrating from react-testing-library to Enzyme
  • Migrating from Enzyme to react-testing-library

Context for React components testing tools

Enzyme was released in 2015. react-testing-library was released in 2018 and gained traction quickly. The latter has shown no signs of slowing down as noted in the 2022 State of JavaScript Survey that shows 95.1% user retention:

react-testing-library vs. Enzyme - LogRocket Blog (1)

Why do we need to test React components?

By testing our React components, we can be certain that they work the way we want them to and that they don’t break when we make changes to the codebase.

Here are some of the key reasons why we need to test React components:

Ensuring correctness

React components are responsible for rendering the user interface of a web application. By testing them, developers can ensure that the UI is displayed correctly and that it responds to user interactions as expected.

Catching bugs early

Testing React components can help developers catch bugs and errors early in the development process. This can save a lot of time and effort, as it is much easier to fix a bug when it is discovered early in the development process.

Improving maintainability

Testing React components can improve the maintainability of a web application. By having a comprehensive suite of tests, developers can be more confident when making changes to the codebase, as they can quickly check if their changes have introduced any regressions.

(Video) React Testing Library vs Enzyme

Without testing, we run the risk of shipping an application that has bugs and errors that could affect the user experience negatively. This is why it’s crucial to test React components during the development process. It helps us identify and fix issues before the application goes live, and also gives us confidence that our code works as expected.

What is the react-testing-library?

The React Testing Library (RTL) is a testing utility for React applications that focuses on testing component behavior rather than implementation details. It includes built-in assertions for checking expected behaviors and a simple and intuitive API for selecting and interacting with elements in your components. Tests become less brittle and more resilient to changes in the codebase by simulating user interactions.

What is the Enzyme testing library?

Enzyme is a popular testing tool for React applications. It provides a set of utility functions for testing React components, similar to React Testing Library. Enzyme, on the other hand, differs from React Testing Library because it focuses on testing the implementation details of your components.

Enzyme includes APIs for mounting, rendering, and manipulating React components in tests. It enables you to inspect and manipulate component internal states and properties, as well as simulate user interactions. This can be beneficial when testing complex components with a lot of internal state or that interact with external services.

Over 200k developers use LogRocket to create better digital experiencesLearn more →

Example React components for testing

Besides the two main differences mentioned, there are several details that might help you choose one tool for your next React project.

To demonstrate that, I’ve come up with a simple component idea implemented through two different approaches: one being a functional component with React Hooks, and the other being a class component. That way, we’ll be able to compare the test structure for each type of component.

If you want to take a look at the entire code (with tests), here’s a GitHub repo you can use alongside this post.

Also, keep in mind that this post does not focus on the setup of any of those tools. If you want to see how that was done, you can look at this other LogRocket post showing what dependencies are needed for each tool.

So, we’re creating a RangeCounter component that should present two control buttons to users (for adding and subtracting) and the current count in between those buttons.

(Video) LogRocket React Meetup: React Testing Overview - January 2022

That count should be ruled by the props passed to the component (min and max).

When the user reaches any of the values in the range limit, they should see an alert message below the counter explaining why they are not able to keep increasing or decreasing the counter.

Example class component

The class component looks something like this:

class RangeCounterClass extends Component { constructor(props) { super(props); const { min } = props; this.state = { counter: min, hasEdited: false }; this.incrementCounter = this.incrementCounter.bind(this); this.decrementCounter = this.decrementCounter.bind(this); } componentDidUpdate() { ... } incrementCounter() { ... } decrementCounter() { ... } render() { const { max, min } = this.props; return ( <div className="RangeCounter"> <span className="RangeCounter__title">Class RangeCounter</span> <div className="RangeCounter__controls"> <button disabled={this.state.counter <= min} onClick={this.decrementCounter} > - </button> <span>{this.state.counter}</span> <button disabled={this.state.counter >= max} onClick={this.incrementCounter} > + </button> </div> {(this.state.counter >= max || this.state.counter <= min) && this.state.hasEdited && ( <span className="RangeCounter__alert">Range limit reached!</span> )} </div> ); }}

Keep in mind that you can always check the GitHub repo for the entire component code.

Example functional component

The functional component will look like this:

const RangeCounterFunctional = props => { const { max, min } = props; const [counter, setCounter] = useState(min); const [hasEdited, setHasEdited] = useState(false); useEffect(() => { if (counter !== min && !hasEdited) { setHasEdited(true); } }, [counter, hasEdited, min]); return ( <div className="RangeCounter"> <span className="RangeCounter__title">Functional RangeCounter</span> <div className="RangeCounter__controls"> <button disabled={counter <= min} onClick={() => setCounter(counter - 1)} > - </button> <span data-testid="counter-value">{counter}</span> <button disabled={counter >= max} onClick={() => setCounter(counter + 1)} > + </button> </div> {(counter >= max || counter <= min) && hasEdited && ( <span className="RangeCounter__alert">Range limit reached!</span> )} </div> );};

Both have the same behavior and will look mostly the same for users (except for the title, which can be ignored for this post’s purposes).

Testing with Enzyme vs. react-testing-library

We’ll be testing the following scenarios for both components with both tools:

  • Testing that a user is able to increment when incrementing is allowed
  • Testing that a user is able to decrement when decrementing is allowed
  • Testing that a user is not able to increment when count reaches maximum
  • Testing that a user is not able to decrement when count reaches minimum
  • Testing that an alert message shows up only after editing and reaching minimum or maximum limit

Testing with Enzyme: Is user able to increment when incrementing is allowed?

Let’s look at the test for the first scenario in the list when using Enzyme:

describe("RangeCounterClass", () => { let wrapper; beforeEach(() => { wrapper = shallow(<RangeCounterClass />); }); describe("when incrementing counter is allowed", () => { it("updates counter value correctly", () => { wrapper.instance().incrementCounter(); expect(wrapper.state().counter).toEqual(1); expect(wrapper.state().hasEdited).toEqual(true); }); });});

You’ll notice that in order to test that the component works correctly, you have to check that the correct props were received and also that the state looks correct. When that test passes, we assume that the current count showing up to the user is the one that is in the counter state variable.

Also, we check if the hasEdited variable changed to true now that we programmatically updated the counter (the value in that state can also tell us whether the alert will show up or not).

More great articles from LogRocket:

  • Don't miss a moment with The Replay, a curated newsletter from LogRocket
  • Learn how LogRocket's Galileo cuts through the noise to proactively resolve issues in your app
  • Use React's useEffect to optimize your application's performance
  • Switch between multiple versions of Node
  • Discover how to animate your React app with AnimXYZ
  • Explore Tauri, a new framework for building binaries
  • Compare NestJS vs. Express.js
(Video) Testing with Jest: From zero to hero

Testing with react-testing-library: Is user able to increment when incrementing is allowed?

Now let’s look at that same test scenario but with react-testing-library:

describe("RangeCounterFunctional", () => { describe("when incrementing counter is allowed", () => { it("updates the counter value", async () => { const { getByTestId, getByText } = render(<RangeCounterB min={2} />); const incrementButton = getByText("+"); fireEvent.click(incrementButton); expect(getByTestId("counter-value").innerHTML).toEqual("3"); }); });});

It’s clear that the idea of this test is to check what is showing up in the UI. That is accomplished by getting the actual DOM element and checking its contents, which represent what the user actually sees.

The next three scenarios in the list display the same kind of pattern. The interesting one to look at now is the last scenario, in which shows that you can also use Enzyme following the same concept of react-testing-library.

Let’s take a look.

Testing with Enzyme: Does a message only show after editing and reaching the limit?

describe("RangeCounterClass", () => { let wrapper; beforeEach(() => { wrapper = shallow(<RangeCounterA />); }); it("shows range reached alert when reached limit by clicking control buttons", () => { wrapper = shallow(<RangeCounterA min={0} max={1} />); wrapper.instance().incrementCounter(); wrapper.update(); const alert = wrapper.find('.RangeCounter__alert'); expect(alert.text()).toEqual('Range limit reached!'); } );});

Testing with react-testing-library: Does a message only show after editing and reaching the limit?

describe("RangeCounterFunctional", () => { it("shows range reached alert when reached limit by clicking control buttons", () => { const { getByText } = render(<RangeCounterB min={0} max={1} />); const incrementButton = getByText("+"); fireEvent.click(incrementButton); expect(getByText("Range limit reached!")).toBeVisible(); } );});

We see that both are strictly confirming that the alert is showing up in the page, but in a slightly different way.

With Enzyme, it’s common to see tests that try to find elements in the page by their class (that is not a rule though), which is not meaningful because users do not see those in the UI. After having the element, you can check the contents of it (which is what the user actually sees).

With react-testing-library, the idea is that you search directly by the actual text that the user sees without the overhead work of finding the element that contains that text.

Imagine a scenario where you have tons of child components and a more tangled HTML structure. You’d probably have more trouble following the same concept when using Enzyme.

After reading this post, you may be wondering if you can migrate your tests from one of these tools to the other. Let’s take a look.

Migrating from react-testing-library to Enzyme

To migrate tests from react-testing-library to Enzyme, you’ll need to install an additional library called enzyme-adapter-react-[react-version]. This adapter library is necessary and there are different setup steps depending on your version. Here is a list with all the versions. However, at the time of writing, Enzyme’s adapters only go up to React v.16. An unofficial adapter exists for React v.17, but none yet for React v.18.

If that is not an issue for you, then install the adapter library and choose your test runner. Enzyme isn’t opinionated and offers many different options (e.g., Jest, Mocha, and others). Here’s a list of all the different guides.

(Video) Capture Frontend Logs & User Insights with Log Rocket & React

Migrating from Enzyme to react-testing-library

Migrating tests from Enzyme to react-testing-library is a little more straightforward. In fact, Kent C. Dodds, the creator of React Testing Library, wrote a complete guide to help developers migrate their tests easily. The guide includes all the necessary installation steps, as well as multiple examples for adapting your tests.

Conclusion

No tool is objectively better than the other: you must consider the variables you have to account for when making a decision about which tool to use.

This specific comparison is based on how developers used to think about tests when using these tools, and how easy it is to follow the idea of testing user behavior instead of component implementation with each tool.

It’s clear that react-testing-library makes that a lot easier with all of the helper methods for querying and the matchers from jest-dom, so it’s natural that you’d want to use that instead.

However, there are limitations to react-testing-library, such as not being able to access component state (which might be intentional because you shouldn’t be doing that, in theory).

However, if you feel like you really need that, then Enzyme may be a better option. Just make sure that you write tests that resemble user experience whenever possible.

LogRocket: Full visibility into your production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time,try LogRocket.

LogRocket combines session replay, product analytics, and error tracking – empowering software teams to create the ideal web and mobile product experience. What does that mean for you?

Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay problems as if they happened in your own browser to quickly understand what went wrong.

(Video) LogRocket walkthrough

No more noisy alerting. Smart error tracking lets you triage and categorize issues, then learns from this. Get notified of impactful user issues, not false positives. Less alerts, way more useful signal.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps —start monitoring for free.

Videos

1. React - Testing w/ React Testing Library
(Anson the Developer)
2. How to test Vite projects using Vitest
(LogRocket)
3. LogRocket for mobile demo
(LogRocket)
4. Modern React with Paige Niedringhaus
(LogRocket)
5. Epic React, quality content, and office hours with Kent C. Dodds
(LogRocket)
6. LogRocket URQL meetup: GraphQL, the URQL library, & building advanced features on the client-side
(LogRocket)
Top Articles
Latest Posts
Article information

Author: Tish Haag

Last Updated: 24/04/2023

Views: 6079

Rating: 4.7 / 5 (67 voted)

Reviews: 90% of readers found this page helpful

Author information

Name: Tish Haag

Birthday: 1999-11-18

Address: 30256 Tara Expressway, Kutchburgh, VT 92892-0078

Phone: +4215847628708

Job: Internal Consulting Engineer

Hobby: Roller skating, Roller skating, Kayaking, Flying, Graffiti, Ghost hunting, scrapbook

Introduction: My name is Tish Haag, I am a excited, delightful, curious, beautiful, agreeable, enchanting, fancy person who loves writing and wants to share my knowledge and understanding with you.