An Intro to React Testing Library

Writing tests for a web application has become crucial as the size and complexity of web application increases

Shreejit
Shreejit
Jun 24 · 5 min read

React Testing Library (RTL) got released as an alternative to Airbnb’s Enzyme. Unlike Enzyme, React Testing Library takes a step back and questions us “how to test React components to get full confidence in our React components”. Rather than testing a component’s implementation details, React Testing Library puts the developer in the shoes of an end-user of a React application.


Jest and React Testing Library both are used while testing React components but most people have RTL confused as an alternative to Jest. This is simply not true as they need each other and both of them have their own specific tasks. Jest is a JavaScript test runner and a JavaScript library for creating, running, and structuring tests.

create-react-app comes with Jest and React Testing Library setups by default with the installation. If you are using a custom React setup, you will have to set up Jest and React Testing Library yourself.

describe('check truthy and falsy values', () => {
test('true is truthy', () => {
expect(true).toBe(true);
});
test('false is falsy', () => {
expect(false).toBe(false);
});
});

Writing a Jest test is very straight forward. If we look at the example above the describe-block is the test suite. The test-block (which also can be named it instead of test) is the test case. A test suite can have multiple test cases and a test case doesn’t always have to be in a test suite. We write assertions (e.g. expect in Jest )to validate if the output matches our exception which either turns out to be successful (green) or erroneous (red).

In summary, Jest is a test runner, which gives you the ability to run tests from the command line. The frameworks offer a lot more than this (e.g. spies, mocks, stubs, etc.); but essentially that’s everything needed for now to understand why we need Jest in the first place. There is nothing about React components yet.

React Testing Library, on the other hand, is specifically for testing React components. As mentioned above it is an alternative to Enzyme and not Jest.


Lets us look at how to render a react component using RTL for your tests. Let's try using RTL on the following App component.

import React from 'react';const title = 'Hello React';function App() {    return <div>{title}</div>;
}
export default App;

And test it in App.test.js file

import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
describe('App', () => {
test('renders App component', () => {
render(<App />);
});
});

RTL has its own render function that can be used to render your component which renders the final output of the component and this is accessible to you for testing.

import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';describe('App', () => {
test('renders App component', () => {
render(<App />);
screen.debug();
});
});

After running the test using npm test command you should see HTML output of your application. screen.debug() method is essentially a shortcut for console.log(prettyDOM()). It supports debugging the document, a single element, or an array of elements. Whenever you write a test for a component with React Testing library, you can render the component first and then debug what’s visible for RTL’s renderer in the test. This way, you can write your test with more confidence.

<body>
<div>
<div>
Hello React
</div>
</div>
</body>

The great thing about React Testing Library is it doesn’t care much about the actual components. As it is only concerned about the output of the rendered component using different React features (useState, event handler, props) and concepts (controlled component) does not make a difference while rendering the final output.

React Testing Library is used to interact with your React components like a human being. What a human being sees is just rendered HTML from your React components, so that’s why you see this HTML structure as an output rather than two individual React components.


After you have rendered your React component, RTL offers you different search functions to grab elements. These elements are then used for assertions or for user interactions.

import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
describe('App', () => {
test('renders App component', () => {
render(<App />);
screen.getByText('Search:');
});
});

Using the screen.debug() function we can clearly see what elements are present in the final output. After you know the HTML structure, you can start to select elements using RTL’s screen object function. The selected element can then be used for user interactions or assertions. We will do an assertion that checks whether the element is in the DOM.

import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
describe('App', () => {
test('renders App component', () => {
render(<App />);
expect(screen.getByText('Search:')).toBeInTheDocument();
});
});

The getByText function accepts a string as input, as we are using it right now, but also a regular expression. Whereas a string argument is used for the exact match, a regular expression can be used for a partial match which is often more convenient.

Similar to getByText function React testing library provides the following functions to selecting an element in the final HTML:

  • getByText
  • getByRole
  • getByLabelText
  • getByPlaceholderText
  • getByAltText
  • getByDisplayValue

queryBy variants:

  • queryByText
  • queryByRole
  • queryByLabelText
  • queryByPlaceholderText
  • queryByAltText
  • queryByDisplayValue

Find by variants:

  • findByText
  • findByRole
  • findByLabelText
  • findByPlaceholderText
  • findByAltText
  • findByDisplayValue

We have seen how we can render as well as search for an element to check if it has been rendered or not using React Testing Library. But what about user interactions? React is used to build dynamics web application intended for a large number of user interactions so testing the React components based on the users interaction event is necessary.

There are two ways to fire an even on a rendered component using RTL. The first method is by using fireEvent() function to simulate interactions of an end-user. Let’s see how this works for our input field:

import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import App from './App';
describe('App', () => {
test('renders App component', () => {
render(<App />);
screen.debug();

fireEvent.change(screen.getByRole('textbox'), {
target: { value: 'JavaScript' },
});
screen.debug();
});
});

The fireEvent() function takes two parameters, an element (here the input field by textbox role) and an event (here an event which has the value “JavaScript”).

The second method to test user interaction on a component is by using an extended user event library which builds up on top of the fireEvent that comes with React Testing Library itself. The userEvent API mimics the actual browser behavior more closely than the fireEvent API. For example, a fireEvent.change() triggers only a change event whereas userEvent.type triggers a change event, but also keyDown, keyPress, and keyUp events.

import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import App from './App';
describe('App', () => {
test('renders App component', async () => {
render(<App />);
await screen.findByText(/Signed in as/);
expect(screen.queryByText(/Searches for JavaScript/)).toBeNull(); await userEvent.type(screen.getByRole('textbox'), 'JavaScript'); expect( screen.getByText(/Searches for JavaScript/)).toBeInTheDocument();
});
});

React Testing Library simplifies the testing process by testing the final output behaviour instead of the internal logic of the component. You are able to not only test the rendered output but also the change in the component by firing an even.

JavaScript In Plain English

New articles every day.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store