How to Test With Jest
Testing React components using Jest and React-Testing-Library.
Hi there, welcome back! Thank you for stopping by. Last month in my Getting Started with AWS article I wrote:
“ If you are a budding developer looking to get that first opportunity, it is important to get familiar with some of the different cloud infrastructure providers out there. ”
The exact same can be said for writing tests for your code and the Testing Frameworks and libraries that help you do it. In this article, we will walk through setting up some unit tests with jest
and react-testing-library
for some components in an existing React application.
What is Jest?
Jest is a delightful JavaScript Testing Framework with a focus on simplicity.
It works with projects using: Babel, TypeScript, Node, React, Angular, Vue, and more!
What is React Testing Library?
The
React Testing Library
is a very light-weight solution for testing React components. It provides light utility functions on top ofreact-dom
andreact-dom/test-utils
, in a way that encourages better testing practices. Its primary guiding principle is:The more your tests resemble the way your software is used, the more confidence they can give you.
So rather than dealing with instances of rendered React components, your tests will work with actual DOM nodes. The utilities this library provides facilitate querying the DOM in the same way the user would.
Let’s get started!
The Application
For this example, we have a simple SPA bootstrapped using the create-react-app your-apps-name
command. The app has 2 simple components, a counter with “up” and “down” buttons. Clicking them increases and decreases the count displayed on the screen. The other component is a basic form with a submit button. Filling the form and clicking submit will display the message typed in the form.
When you create projects using theCreate React App
command, you have out of the box support for React Testing Library and Jest. If you didn’t use that command, you can add them via npm
like so:
// Install React Testing Library
npm install --save-dev @testing-library/react// Install Jest
npm install --save -dev jest
The Components:
As mentioned, our first component is the Counter
component.
As you can see, it is a very simple component that manages state locally with the useState
hook(line 5 and 6). It has a single arrow function called handleClick
(line 8) that manages both button clicks with some conditional statements. It also removes the element from the screen based on a nested conditional statements(line 17).
Our second component is the Printer
component.
This component has a bit more going on. Again, we manage all our state attributes with the useState
hook(lines 4 – 7). In this case, we have a handleChange
arrow function (line 9) that manages input changes on the form when the user is typing in the input field. Finally, we have a handleClick
arrow function (line 13) that triggers all our state changes. Also, this component disables the “Print” button if there is no data in the input field.
The App Test
Since our app was bootstrapped with create-react-app
there is already one test file available for us in the src
directory of the app called App.test.js
. Let’s break this down a bit.
This is the test file for the App.js
component. Notice that like any other React component it starts by importing React (line 1). Then we import the necessary functions from React Testing Library(line 2) followed by importing the component to be tested(line 3).
As the test indicates it is expecting that our App component “renders learn react link” (line 5). To verify this we need to destructure the getByText
query and make it equal to the render
function we imported and we pass it our component(line 6). This basically tells our query where it is going to be looking. Then we create a variable and assign it the return value of the getByText
query and pass in the text we are looking for. In the example above, line 7 is passing in a regular expression of the desired text. Now that the query is ready to go the expectation can be set. Here, we expect the text “learn react” to be in the document. This is the basic structure of the tests we will be writing.
// To run tests use these commands// If using yarn
yarn test// If using npm
npm test
Currently, this test is failing because we changed all the code this was testing for. If you are following along, you should see something like this.
Let’s update our test so we are looking for stuff that actually exists in our document.
Ok, above we have the same test but we added some new variables with a few examples of different ways we are able to search for text. Line 11 shows an example that searches for a string and uses a flag to ignore case sensitivity. Lines 14 and 15 show 2 ways of using regular expressions. Line 15 is looking for a full string match while ignoring case and line 14 is looking for substrings while ignoring case. Now, when we run our test it passes.
The Counter Test
Let’s break down the steps to writing your test from scratch.
1 - Create a test file
Create a new file in the src
directory with the name of the component to be tested. All test files should follow the same naming convention ComponentName.test.js
. In our case, we are testing the Counter
component so our file will be called Counter.test.js
.
2 - Import React, components, and functions
As we did before we go through our imports and this time we also include the FireEvent
function from React Testing Library. This will allow us to simulate button clicks in our tests.
3 - Write functionality tests
With everything in place, we can start writing our test. Since we are already checking that our components render correctly in our App.test.js
file, we can test the functionality of the buttons by testing their individual actions and results.
Testing the “Up” button
In this test, we are making sure that when we click the “Up” button for the first time we get the number 1 printed on the screen. To achieve this, we will be looking at multiple values.
- The text on the button.
- The text we are looking for as a result of clicking the button.
This means we will need to use a different query function other than getByText
. Thankfully, there is a very flexible one called getByTestId
that allows us to search by an id we create on the component we are testing using the data-testid=
attribute.
Then in our test, we can access that id like this.
On line 14 we destructure our getByTestId
query and pass it as an argument to our fireEvent.click()
function and include the testId
we created.
With that test passing, we can pretty much do the same for the “Down” button.
In order to be able to test that the “Down” button decrements the count correctly we have to increment the count first so we added an extra click event on line 26 then we check that the number incremented and then we fire the down click event on line 34.
The Printer Test
Our Printer
component has a text input and a submit button. We are going to test a few things here.
1 - Repeat steps 1 and 2 from the previous test.
src
directory.2 - Start writing tests
We will test a couple of things for each part of this component. We can keep things organized by using Jest’s describe
blocks to group together several related tests.
Input tests
In this example, want to make sure the input field renders. We also so want to test if the input field updates when the user types into it.
We will start with a new describe block.
We’ll group all our input tests inside this block. Our first test will check that the input field renders correctly.
React Testing Library has many queries at our disposal. Here is a very handy cheatsheet. Since we are looking for the text in the component and our component is just the input and buttons we can use the getByPlaceholderText
query. Note that this is not the best practice. Normally, we would be using form tags and there would be labels for each input of the form, etc.
A placeholder is not a good substitute for a label so you should generally use
getByLabelText
instead.
So far all our tests should be passing.
Now we can move on to testing if it updates on change.
As you can see on line 29, we are using a different event called “change”. This allows us to simulate a user inputting text into a form or input field.
Now our describe block should look like this.
And all our tests are passing.
Now we want to make sure that our Print
button works as expected. To do that we need 2 tests, one for each case. The first case is when the button is clicked with some data in the input field. The second case is when there is no data in the input field. To make these tests a bit more descriptive we can wrap each test case in a describe block. This way we are able to state each case individually.
Then we want to add another test inside this describe block to make sure our “Print” button is not disabled when there is data in the input field.
Our “print button” describe block should now look like this. I will remove the notes just so it all fits in the image.
Now, we can add our second case inside its own describe block. Here we want to check that the button is disabled when there is no data in the input field.
This is what our finished “print button” block should look like.
And our tests are all passing!
Just like that, we have written 3 test suites with a total of 8 tests for App and our 2 components. Here is the working app.
I hope this was fun and informative. I’d love to hear your thoughts in the comments. As always, stay healthy, stay curious!