Setting up test files in React with Jest
Tags: react, testing, jest
Published: 2024-02-29 20:25:52

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"
```

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";

```

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;
```

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
});
```

Let's go through each section. 

```
test('renders the correct initial message and updates it on button click', () => {
  // Render the App component
  render(<App />);
```
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 />);
```

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'); 

```
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>
```

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);
```
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>
```

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
```
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.

```

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.
```

## 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](https://www.digitalocean.com/community/tutorials/how-to-test-a-react-app-with-jest-and-react-testing-library)
[Smashing Magazine](https://www.smashingmagazine.com/2020/06/practical-guide-testing-react-applications-jest/)
[BrowserStack](https://www.browserstack.com/guide/react-app-testing-with-jest)
[NetNinja](https://youtube.com/watch?v=l9qr3EuLE_8&list=PL4cUxeGkcC9gm4_-5UsNmLqMosM-dzuvQ&index=7)
[ChatGPT](https://chat.openai.com/c/42948249-c92d-4c0e-a227-da3a350248a3)
[Canvas](https://learning.flatironschool.com/courses/7053/assignments/265166?module_item_id=635150)