Testing Components and Accessibility with React Testing Library

Mario Santos
iFood Engineering
Published in
6 min readJun 22, 2021

Hi everyone! I’m Mário, a software engineer at @ifood.

In this article, we are going to talk about tests. We’ll use React Testing Library, which we can write simple and confident tests. To follow this article you need to have some knowledge of React and Jest.

We’ll discuss:

  • Render
  • Event Handlers
  • Async updates
  • Mock
  • Accessibility

First of all, we need to install NodeJS and create a React App. Here, we will use create-react-app typing the following command on terminal:

npx create-react-app rtl-demo

The first step in learning how to write great test scripts with React Testing Library is to understand the purpose of React component tests.

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

It’s a simple concept, yet a powerful one. Of course! Testing if users can do what they want to do is what really matters! In this post, we aren’t going to focus on developing software, we’ll study use cases, which will help you test your software on your own.

So, let’s meet some features.

Render

In React Testing Library, we always go render components, you will see it in the next topics. To understand it, we have the component below:

Let’s write a simple test to FavoriteFoodList.

We import render and screen from @testing-library/react.

  • Render will mount our component in a fake DOM
  • Screen is the tree where the render goes to mount our component

Our component is in DOM and a way to test it is to count how many items we have — and to do it we used a screen’s function called getAllByRole, in order to get all li elements in our DOM. To know more about query selectors, you can go to React Testing Library Docs.

You may be wondering, “Why is he using getAllByRole instead getAllByTestId?. I follow the guide of priority suggested by React Testing Library, where getAllByRole is more accessible than getAllByTestId.

Here are som following tips.

Not

To make sure our test can be failed, we can test the opposite assertion, just add “not” to assert to reach it.

And your test will fail.

So now, we’re secure that the test should be able to fail. :)

Debug

Sometimes, we want to debug DOM content to check rendered components. We can simply use screen.debug().

and it shows in the console our DOM.

Now that we have our list tested, let’s understand how to test user actions.

Event Handlers

Actions made by users dispatch events on an element, and we can handle these events, and execute some logic. Let’s see how can we simulate and test it using a form. We will use userEvent from @testing-library/user-event, so, let’s create a new component called FavoriteFood.

Let’s look at this component. We have a form that is going to add a new favorite food with the following rule:

The add button needs to be disabled until user type some value in favorite food field.

To do it, we create a state to value and passed the disabled attribute to the button. You can write two tests to reach the above rule.

The first test is simple, we are checking the component initial rendering.

And in the last test we are simulating user typing in the input field, which means type method is simulating some events like over, focus, blur… It’s amazing because if we change our function handleChange to handleFocus and call it on input focus, the test is keep passing.

With userEvent we can simulate any done actions by users.

Async updates

Sometimes, we need to test async updates in components. So let's change the component to get list dynamically.

Now we have a state to show favorite foods, we can also create a method getList to get asynchronous data from service. The getFavoritesFood method is returning a simple array promise, like our old list, and its implementation doesn't matter now.

If we run our test, we have the following result:

This log is that our getAllByRole doesn’t find any listitem in the component.

This other log is that when our test is completed, our component has a pending update, and React Testing Library tells us to put it inside an act function. So, React Testing Library gives us some utils to work with that.

You can see all utilities in documentation, but here we will use waitFor function to wait our expect passes. Let's see how our test will be:

Since we are working with async updates we need to change our function to asynchronous, so put an async word before the test function, and do the same in waitFor function — just change it to an await word.

Mock

Sometimes we pass function through the component props. To test it, let’s change our FavoriteFood to receive a submit function through the props.

In this case, we will use jest.fn to mock onSubmit function and check if it’s called correctly.

We added a new test to the above component, so we have a mocked function called onSubmitMock and we passed it to FavoriteFood component. In the next lines, we add a click button to submit the form. Let’s understand the assertions:

  • toHaveBeenCalled check with our mock has been called;
  • toHaveBeenCalledTimes same to toHaveBeenCalled, but it checks how many times the mock has been called;
  • And toHaveBeenCalledWith check parameters passed to the mock.

The icing on the cake: Testing Accessibility.

To test accessibility we need to add jest-axe in our application. But that doesn’t guarantee your application is accessible, it just checks your HTML markup. First, we need to add jest-axe to the project.

yarn add -D jest-axe

Now, let’s write a simple component:

To see the accessibility violations we can write the following test:

We extended toHaveViolations function to that so we can use it with jest expect. Now, just run the test.

Here we have a violation in our input — to fix it, we can just choose one suggestion of the list for the test, so let's set a placeholder to input and run the test again.

Conclusion

Today we learned a bit of how to test using React Testing Library. You saw how to render components, dispatch events, async updates, mock data, and accessibility. At this point, you are able to test units and integrations in your application.

The next step is to understand the best practice using React Testing Library, I really recommend the Kent C. Dodds article. React Testing Library is focused on components tests, if you want to test hooks you can use @testing-library/react-hooks.

So, that’s it, I really expect that you enjoyed the content. I tried to show you how I use React Testing Library here at iFood.

This article was inspired by Kent C. Dodds course.

Complete code can be found on GitHub.

Take a look at the vacancies available in iFood and learn more about the selection process.

--

--