Testing React Applications with react-testing-library
source link: https://www.tuicool.com/articles/hit/RFRNbeZ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
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.
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: Bit ( GitHub ) is a useful way to organize your React components into avisual catalog, and consume them from different projects. Instead of rewriting components, you can share, reuse and develop them anywhere.
Writing our first test
Let’s take the simplest component of them all, the TodoListItem
component and add test coverage to it.
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 takes 3 props:
- item: which is the actual todo item and has three attributes:
index
,value
anddone
-
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.
Again the component code is listed above for easy reference. The test for the same is below:
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!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK