How to Test With Jest

Testing React components using Jest and React-Testing-Library.

EarthCtzn
The Startup
9 min readSep 7, 2020

--

Photo by Marta Branco from Pexels

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 of react-dom and react-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.

  1. The text on the button.
  2. 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 getByTestIdthat 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.

Create the test file for the component in the src directory.
Set all imports.

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!

--

--

EarthCtzn
The Startup

Full-Stack web developer having fun with Rails, JavaScript, HTML, CSS, React, Redux, Bootstrap, and making things.