Testing React Applications with react-testing-library

A hands-on guide with live examples

RC
Bits and Pieces

--

Have you ever had the feeling that your react component test code is just testing the UI framework and component coupling, but is no where close to mimicking the actual user behavior. If you are with me on that, read further to get introduced to another way of testing components using a library called react-testing-library.

The library was conceptualized by the awesome Kent C. Dodds and I like the guiding principle for creating it:

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

Let’s learn how the testing library helps us write unit/integration tests by taking a simple Todo app as example.

You can play with the app on codesandbox.io. All the code snippets discussed in the post are available in this example repo.

To understand the structure of the app, the component breakup has been annotated in the following diagram.

Todo App Component Breakup

We have a TodoApp container component that has a TodoList which is collection of all existing todos each represented by the TodoListItem. Each TodoList item has a delete button to its right and mark as completed button to the left. The TodoForm component at the bottom allows the user to add new items to TodoList.

Tip: Use Bit to reuse components and build faster! Easily organize and use your components to build more apps with your team. Give it a try.

React spinners with Bit: Choose, Play, Install

Writing our first test

Let’s take the simplest component of them all, the TodoListItem component and add test coverage to it.

TodoListItem.jsx

The implementation code for TodoListItem is listed above so that you can relate to it more easily when walking through the test code below.

TodoListItem.test.js

TodoListItem takes 3 props:

  • item: which is the actual todo item and has three attributes: index, value and done
  • markTodoDone function prop that will be called when a todo is marked as complete
  • removeItem function prop that will be called when a todo is deleted

Let’s start dissecting the test code to understand what is happening there:

The first step would be actually rendering the component and that is what this line does:

const {getByTestId} = render(<TodoListItem item={item} index=
{itemIndex} markTodoDone={markTodoDone}
removeItem={removeItem}/>)

The render method returns an object that has multiple getByXxx helpers to let you select a specific element in the rendered component and evaluate its attributes.

In our case, we are using one such helpers called getByTestId, which is basically a shortcut to [data-testid=<elementId>] If you check our component code, you would see data-testid added to various elements for each testing.

You can use the other selectors as well if you are not willing to add an explicit data attribute for testing. Listing few of the helpers:

  • getByLabelText: search for the label that matches the given text
  • getByPlaceholderText: search for elements by their placeholder attribute
  • getByText: search for elements that have given textual content

react-testing-library uses dom-testing-library (by the same author) behind the scenes to offer DOM testing utility functions for easy querying. You should note that for each of the above get helpers, there are matching getAll API that returns all elements instead of just the first one, and query/queryAll that return null/[] instead of throwing an error.

You see that in our test, we use the getByTestId helper to assert if TodoListItem renders item.value correctly.

Firing Events

To fire events like click or change , we should use one of the above selectors to retrieve the element and then use the fireEvent helper to mimic user interaction on the component.

In our example, this piece of code does exactly that…

fireEvent.click(getByTestId('markAsCompleted'))
expect(markTodoDone).toBeCalledWith(itemIndex)

We select the mark-as-completed tick box using its test id and then fire a click event on that. We have passed a mock jest function as prop when rendering the component and asserting if it has been called would be good indication of the interaction wiring!

Testing Form Interaction

Let’s go over TodoFormTest.js code to understand how to test form elements.

TodoForm.jsx

Again the component code is listed above for easy reference. The test for the same is below:

TodoForm.test.js

We will use fireEvent.change to enter value into the new todo input element and then click on the add button.

In this test, we assert that the jest mock callback function is called along with the value that is entered in the input element

If you have read this far, you would have noticed that in all of the tests, we render the actual component with all its children and dependencies and nothing is mocked out. In react testing parlance, we did not do shallow rendering. I do not recommend using shallow testing and you can read through this excellent post to understand more.

Please do browse through the other examples in the repo to know more about what react-testing-library can do. Feel free to comment and ask me anything!

--

--

Full Stack Engineer @ Pro.com , Ex-ThoughtWorker • Ruby,JavaScript, iOS, Frontend, Devops https://rcdexta.com