DEV Community

Katie N
Katie N

Posted on

Setting up test files in React with Jest

When creating applications, a skill that will help create scalable and properly working code is the ability to write tests. Unit testing allows us to test the individual functionality of our React components. Learning how to write tests can be instrumental in debugging quicker and writing good dynamic code. In this post we're going to talk about how to use Jest testing framework with React. If you're familiar with React, you know how powerful and intuitive it is. Well, Jest is just like that - powerful and intuitive. We will be using the Node package manager for this blog but yarn works similarly.

Getting Setup

When starting a React project, a popular way to go about it is to run npm create-react-app. Starting your repo with this command will automatically install Jest so there is no need for any extra install/download work. Double check your package-json folder to make sure you have the proper packages installed. If it is not already installed you can run npm install --save-dev @testing-library/react to install.

  "name": "client",
  "version": "0.1.0",
  "private": true,
  "proxy": "http://localhost:3000",
  "dependencies": {
--->"@testing-library/jest-dom": "^5.17.0",
--->"@testing-library/react": "^13.4.0",
--->"@testing-library/user-event": "^13.5.0",
    "bootstrap": "^5.3.3",
    "react": "^18.2.0",
    "react-bootstrap": "^2.10.1",
    "react-dom": "^18.2.0",
    "react-router-dom": "^6.22.1",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "devDependencies": {
    "@babel/plugin-proposal-private-property-in-object": "^7.21.11"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
--->"test": "react-scripts test",
    "eject": "react-scripts eject"
Enter fullscreen mode Exit fullscreen mode

You can see under our "dependencies" there is the Jest package installed and the "test" line in our "scripts".

Test Structure

The best way to set up your test files is to create a folder in your /src folder and name it __tests__. In this folder you will keep your test files. When you create your file, the best practice is to name your test file almost identical to your component file name. For example, if we were going to start writing tests for our App.js file, we should name our test file App.test.js.

Navigate to App.test.js and in the top of the page import the libraries you need for testing. It will typically look something like --

// import libraries needed for testing
import "@testing-library/jest-dom";
import { render, screen } from "@testing-library/react";
import React from "react";

// import the component you wrote
import App from "../components/App";

Enter fullscreen mode Exit fullscreen mode

Often the test file will already be started for you automatically, but you should always double check.

Jest tests are written in test blocks. They consist of 4 parts.

  1. Render the component you want to test.
  2. Find elements of the component you want to interact with.
  3. Interact with the element and establish expectations.
  4. Assert 'expected' results.

For this example, let's use the tried and true "Hello World" example. We have a simple App.js component --

import React, { useState } from 'react';

function App() {
  const [message, setMessage] = useState('Hello World');

  return (
    <div>
      <p data-testid="message">{message}</p>
      <button onClick={() => setMessage('Button Clicked')}>Click Me</button>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

And then we will have our App.test.js file --

import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import App from './App';

test('renders the correct initial message and updates it on button click', () => {
  // Render the App component
  render(<App />);

  // Find the element that displays the message
  const messageElement = screen.getByTestId('message');
  expect(messageElement).toHaveTextContent('Hello World'); // Initial expectation

  // Find the button and click it
  const buttonElement = screen.getByRole('button', { name: 'Click Me' });
  fireEvent.click(buttonElement);

  // Assert the message has changed after clicking the button
  expect(messageElement).toHaveTextContent('Button Clicked'); // Updated expectation
});
Enter fullscreen mode Exit fullscreen mode

Let's go through each section.

test('renders the correct initial message and updates it on button click', () => {
  // Render the App component
  render(<App />);
Enter fullscreen mode Exit fullscreen mode

Each test will start with test() or it() (used interchangeably) to provide context around what you plan to use the test for. You can also take it a step further and include a describe() above the test() line to further describe the test or name it.

describe("correctMessage", () => {
    test('renders the correct initial message and updates it on button click', () => {
      // Render the App component
      render(<App />);
Enter fullscreen mode Exit fullscreen mode

Next up we find the elements we want to interact with.

  // Find the element that displays the message
  const messageElement = screen.getByTestId('message');
  // Initial expectation
  expect(messageElement).toHaveTextContent('Hello World'); 

Enter fullscreen mode Exit fullscreen mode

We will use screen to filter through the component and pluck out what we're wanting to test. We're looking for .getByTestId so let's look back up at our App.js file and see what is being pulled into our test.

In our App.js file we see this line --

<p data-testid="message">{message}</p>
Enter fullscreen mode Exit fullscreen mode

This is what is being pulled by our test to be interacted with.
In the next line we are declaring what we initially expect from this element. We expect the message to say "Hello World".

Then we go down a little in our test and see the next expectation of our test.

  // Find the button and click it
  const buttonElement = screen.getByRole('button', { name: 'Click Me' });
  fireEvent.click(buttonElement);
Enter fullscreen mode Exit fullscreen mode

Same thing as before. Pulling the element we want to test through screen and declaring our initial expectation. This is the element we are pulling this out of our App.js component --

      <button onClick={() => setMessage('Button Clicked')}>Click Me</button>
Enter fullscreen mode Exit fullscreen mode

We are expecting this button to be able to do something. To click.

note: the fireEvent function is used to mimic a call to action for your test.

Once we have pulled our elements and gave them initial expectations, we're going to assert what we want our final expectation to be of how those elements tie together to do what they're supposed to.

  // Assert the message has changed after clicking the button
  expect(messageElement).toHaveTextContent('Button Clicked'); // Updated expectation
Enter fullscreen mode Exit fullscreen mode

We expect our button to display "Hello World" when it is pressed.

Refactoring and npm test

Once you have your tests written you'll want to actually implement them. Assuming you have already ran npm install in your terminal, you're going to run npm test. You should receive an output of all your passing and failing tests as well as a CLI menu to be able to interact with your terminal while your tests are being watched and ran.

> your-app-name@1.0.0 test
> jest

 PASS  src/App.test.js
  ✓ renders the correct initial message and updates it on button click (32 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.357 s, estimated 2 s
Ran all test suites.

Enter fullscreen mode Exit fullscreen mode

If you notice that your tests are failing, you will have to go back into your App.js file to refactor your code until your tests are passing. Unless you exit out of the test suite it will update and run every time you save your file.

To be able to navigate through the test suite here is the CLI menu as well.

Watch Usage
 › Press a to run all tests.
 › Press f to run only failed tests.
 › Press p to filter by a filename regex pattern.
 › Press t to filter by a test name regex pattern.
 › Press q to quit watch mode.
 › Press Enter to trigger a test run.
Enter fullscreen mode Exit fullscreen mode

Conclusion

I hope now you have a better understanding of how to implement testing into your code. It is so helpful and time saving in the debugging process allowing you to focus on the creativity.

Sources

Digital Ocean
Smashing Magazine
BrowserStack
NetNinja
ChatGPT
Canvas

Top comments (2)

Collapse
 
fpaghar profile image
Fatemeh Paghar

Fantastic guide on setting up Jest tests in a React project! Your step-by-step explanation is clear and provides a solid foundation for anyone looking to start testing their React components. I appreciate the emphasis on the importance of testing for creating scalable and reliable code.

Collapse
 
arndom profile image
Nabil Alamin

Thanks for this, could you recommend a course or resource for learning to write tests