Front-end Development
6 min read

Introduction to React Testing Library

Learn how React Testing Library helps developers write unit and component tests that more closely resemble how users interact with the application.

Marie Starck
Published July 28, 2022

Introduced in 2018 by the developer Kent C . Dodds, React Testing Library is a set of testing utilities that quickly made a splash in the React developer community. Contrary to React testing approaches up to that point, Kent pushed a different view of testing which can be summarized by this tweet:

The more your tests resemble the way your software is used, the more confidence they can give you.

This was a clear departure from the approach of existing front-end testing libraries which tended to test implementation rather than what the user was actually seeing.

Kent’s philosophy quickly gained popularity. Today, React Testing Library (sometimes abbreviated as RTL) receives more than 6 million downloads every week.

In this article, you will first discover what problem React Testing Library is trying to solve. Then, you will learn about DOM Testing Library, which is a base set of testing utilities upon which React Testing Library is built. Following this, you will go through the installation process. Finally, you will understand when React Testing Library is helpful and when it is not.

The problem React Testing Library solves

To better understand React Testing library, it’s important to understand the context in which it was created. Before React Testing Library, developers tended to write front-end tests that test the implementation of components. Take a button as an example.

import { useState } from "react";
export default function Button() {
  const [disabled, isDisabled] = useState(false);

  const handleClick = () => isDisabled(true);

  return (
    <button onClick={handleClick} disabled={disabled}>
      Click
    </button>
  );
}

It’s a very straightforward component. It has a disabled variable which is a boolean, and ahandleClick function that sets the disabled variable to true. Both are passed to the <button>. In the end, when the user clicks on the button, the variable is set to true and the button is disabled.

In other testing frameworks, a test of this component would verify the button’s behavior by checking its internal state. Namely, when first rendered, disabled is false, and then when calling handleClick, disabled becomes true.

But consider what happens if someone accidentally deletes the disabled={disabled} on the button element. The test would still pass because the state is as expected. When first rendered, disabled would be false, and calling handleClick would set it to true correctly. Unfortunately, from a user’s perspective, the button doesn’t actually become disabled. In this scenario, this component is very much broken, but you won’t find that out until someone reports it.

A better test would have been to grab the button element in the DOM and check that it has the correct disabled attribute set. This is what Kent C. Dodds meant in his tweet that advises writing tests from a user’s perspective rather than based on the underlying implementation.

But how can you grab DOM nodes easily? Enter the DOM Testing Library.

DOM Testing Library

Anyone who has done jQuery or tried selecting DOM nodes using native Javascript APIs can tell you it’s not easy. First, you have to get familiar with all the different ways you can select nodes whether by types (p, td, th, tr, …), by ids, or classes. Then, you need to learn how to parse them to get the text, the attributes, the roles, and more.

It’s complicated, and one of the reasons the DOM Testing Library is such a godsend.

Note: Having said that, if you are unfamiliar with the different selectors, do not hesitate to read MDN’s documentation on CSS selectors. Even if the React Testing Library will help you with this, it is important to know and understand all the different ways you can select a DOM node.

The DOM Testing Library is a lightweight library that allows developers to easily query DOM nodes for testing. It comes with a suite of methods for you to use such as:

On top of these methods, the DOM Testing Library has another interesting advantage. It was built in such a way that it could be used in combination with different popular frameworks. This versatility has allowed the creation of multiple testing libraries built on top of this one, not just for React, but also for technologies such as Vue and Angular.

As a result, it doesn’t matter the implementation of the technology, because in the end, what is tested is the rendered application. Whether you use React, Angular or Vue, the result is a DOM tree composed of nodes. This is what the DOM Testing Library uses.

How to install React Testing Library

If you use Create React App to set up and manage your React application, React Testing Library comes pre-installed.

Otherwise you can easily install it by running the following command:

    npm install --save-dev @testing-library/react

In addition to installing the base library, you should also install:

Again, these two libraries come pre-installed with Create React App.

Once these libraries are installed, no configuration is necessary as React Testing Library should work out-of-the-box.

Note: For developers coming from Enzyme, the React Testing Library’s documentation has a great guide on how to transition. Don’t hesitate to check it out here.

React Testing Library uses cases

In general, React Testing Library is a great choice for testing components in isolation. Unit testing is a type of software testing where a single unit (or in the case of React, a component) is tested.

Take the Button component for example. With this library, you can quickly test all the necessary behaviors of this component. Here is an example of some tests:

//Button.test.js
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Button from "./Button";

test("renders button with the right text and not disabled", () => {
  render(<Button />);
  const buttonElement = screen.getByText(/Click/i);
  expect(buttonElement).toBeInTheDocument();
  expect(buttonElement).toBeEnabled();
});

test("disable button when clicked", () => {
  render(<Button />);
  const buttonElement = screen.getByText(/Click/i);
  userEvent.click(buttonElement);
  expect(buttonElement).toBeDisabled();
});

Integration and end-to-end testing, on the other hand, is not a great fit for React Testing Library. Let’s say you wanted to test the success case of a form submission. React Testing Library does not provide facilities for mocking API calls, and you wouldn’t want the real API methods invoked when submitting the form.

Additionally, since React Testing Library re-implements the browser’s DOM APIs to run inside Node’s runtime, there can be differences between that behavior of an app when running tests vs. the behavior for real users. Since RTL is not actually driving the browser, it would be difficult to test more complex scenarios or test behaviors like redirection and page navigation. This is why, in the case of end-to-end testing, React developers tend to turn to tools made specifically for this purpose.

Conclusion

This article was an introduction to the React Testing library. You first discovered what problem this library was trying to solve. Then, the DOM Testing library on which the React Testing library is built on was discussed. Finally, you learned how to install the React Testing Library in your project and what are the best use cases for it.

Testing can sometimes be a last-minute thought for some developers. However, great tests can guarantee a better quality for your product and save you lots of debugging time in the future. May this article help the future-you by avoiding bugs in the present.

Get started with Reflect today

Create your first test in 2 minutes, no installation or setup required. Accelerate your testing efforts with fast and maintainable test suites without writing a line of code.

Copyright © 2022 Reflect Software Inc. All Rights Reserved.