React Unit Testing (Tutorial)
Tags: react, redux, javascript
Published: 2020-09-04 00:09:30

## Introduction

Let's face it -- There is no better way to test your applications better than putting them to, well, the test! We can build all day long and visually get the results we are looking for. But, what happens when there is a hidden bug? What if this bug reveals a pretty serious vulnerability? All of this can be avoided in our React apps using unit testing. 

## Necessary Dependencies 
Okay, so just what ARE the needed dependencies and how do we install them? Don't worry -- I'm including this valuable information right here and now. We are going to need a total of three dependencies, so let's start by installing those first:
* `npm install jest-dom`
* `npm install react-test-renderer`
* `npm install @testing-library/react`

Even if you have any of these dependencies, it is ALWAYS a good idea to make sure your versioning is up-to-date. If your application was created using `create-react-app`, you are likely already set up with `@testing-library/react` since it is used for testing your default `App.test.js` file which comes with the initial setup.

## How To Do Your Own Testing
I don't wanna be the guy to put you to work, but it's very important for you to follow along in code as you read. Unit testing isn't difficult, but it can be a little confusing and overwhelming if you try to understand it based on reading alone.

Okay, so let's get right down to it!

### Application Setup (for testing)

Start by creating a new folder under `src` called `components`. Within this folder, create another folder named `buttons`. From here, create two files in your `buttons` folder. They are `button.js` and `button.css`.

Inside of `button.js`, place the following code:
```
// /src/components/buttons/button.js
import React from 'react';
import './button.css';

function Button({label}){
    return <div data-testid="button" className="button-style">{label}</div>
}

export default Button;
```

Here, we are using a functional component that is taking `{label}` as a prop. You'll also notice we are using `data-testid="button"`. `data-*` is an HTML attribute we can use for testing, and this is particularly useful for when another dev comes along and changes the name of your IDs or classes. You can look up `data` for more information but for those on limited time, [this is a great source that sums up the concept](https://www.w3schools.com/tags/att_data-.asp).

Okay, so let's visit roughly the top level of our application (`App.js`). Apply this code:

```
// App.js
import React from 'react';
import Button from './components/buttons/button';

function App() {
  return (
    <div className="App">
      <header>
        <Button label="click me please"></Button>
      </header>
    </div>
  );
}

export default App;
```

The `div` with the class "App" isn't important, **but at this point, you should delete `App.test.js` as editing `App.js` will beak the testing later. We do not need `App.test.js` for this tutorial.** 

Next, we will head back to our `buttons` folder and open `button.css`. Place in the following code:

```
// /src/components/buttons/button.css

.button-style {
    border: 1px solid grey;
    padding: 10px;
    text-align: center;
}
```

This part about adding CSS styling isn't really necessary unless you plan on starting the application to get a visual of your rendered functional component. It was only included in the tutorial for fun! :) 

## Unit Testing Time

Finally, the fun part! In your `src/components/buttons` folder, create a new folder named `__test__`. Inside of this folder, we are going to create a file named `button.test.js`. When your unit testing begins, it will travel down the tree of your application looking for any files with `.test.js` as the extension. This information will be important and further explained shortly.

Inside of `button.test.js`, we want to start with some basic imports at the top of our file. It should look like this:

```
// /src/components/buttons/__test__/button.test.js

import React from 'react';
import ReactDOM from 'react-dom';
import Button from './../button';
import { render } from '@testing-library/react';
```
**Please check React docs and Google if you are not familiar with imports within your application tree. If you're developing in React, you should already know how to use them.**

Alright, so we've taken `{ render }` from `@testing-library/react`. We are immediately going to use this in our first test *below our imports*.

```
// uses @testing-library/react
it('renders without crashing',  () => {
    const div = document.createElement("div");
    ReactDOM.render(<Button></Button>, div)
})
```

`it()` takes two arguments. We are giving the test a description string for the first argument to "renders without crashing", and then an anonymous function for the second argument which will be responsible for returning a boolean if the function executes without issue. To put it in English, we are setting a variable `div` assigned to `document.createElement("div")`. Then, we are rendering our Button component to the DOM.

To run this first test, go ahead and type `npm test` in your IDE terminal and press Enter when prompted. Go ahead, I'll wait. :) 

... 

Your first test has passed! We have validated that an element can be rendered without crashing the application. Well done! To close out your testing, just press `CTRL + C` in your IDE terminal. Now we can move on to unit testing for the present values in our DOM elements.

We are going to need another import. Go ahead and add this to your imports at the top:

```
// /src/components/buttons/__test__/button.test.js

import React from 'react';
import ReactDOM from 'react-dom';
import Button from './../button';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect'; 
// New import ^
```

In `@testing-library/jest-dom/extend-expect`, we are gaining access to the `expect()` function that comes with the `jest-dom` dependency.

Next, add this test below your first one:
```
//uses @testing0library/jest-dom/extend-expect
it('renders button correctly', () => {
    const { getByTestId } = render(<Button label="click me please"></Button>)
    expect(getByTestId('button')).toHaveTextContent("click me please")
})
```

This is where we are putting our `Button` component to the test! We are creating a button under the variable `getByTestId` and a label of "click me please" and *expecting* it to contain "click me please". This test would fail if we could not pass our props down into our component. However! Go ahead and run your tests again. Surprise! They passed again!

We have a problem though... Take a look at these two tests below:

```
//uses @testing0library/jest-dom/extend-expect
it('renders button correctly', () => {
    const { getByTestId } = render(<Button label="click me please"></Button>)
    expect(getByTestId('button')).toHaveTextContent("click me please")
})

it('renders button correctly', () => {
    const { getByTestId } = render(<Button label="save"></Button>)
    expect(getByTestId('button')).toHaveTextContent("save")
})
```

Notice anything strange? They are both the same test, but with different prop values being tested between them. 
if we try to run our tests, we get an error! `found multiple elements by [data-testid="button"]`. What we need to do is include some way to *clean up* our tests when we're done with each one. Luckily, this is very easy and simple to do.

Remember our import friend at the top `import { render } from '@testing-library/react';`? We're going to make one small change:

`import { render, cleanup } from '@testing-library/react';`.

Then, right below your imports and above your tests, include this line:

`afterEach(cleanup)`

Now you can run your tests again. Check it out, they're passing again! 

For one final lesson, we're going to be introduced to JSON snapshots of our tests. These are useful as *snapshots* create an instance of our passing tests and compares that snapshot to future tests to make sure they match. 

Start with adding our final import at the top of our file:
`import renderer from 'react-test-renderer';`

Now that we have `renderer`, we're going to write our final test. Place this last test at the bottom of your other tests:

```
// uses renderer to create a snapshot of the Button component
it('matches snapshot', () => {
    // creates snapshot and converts to JSON
    const tree = renderer.create(<Button label="save"></Button>).toJSON()
    // expects the snapshot to match the saved snapshot code found in the __snapshot__ folder
    expect(tree).toMatchSnapshot()
})
``` 

As mentioned above, we have created a *snapshot* of our tests that will be used to compare to other test runs. You can find this new snapshot under `/src/components/buttons/__test__/__snapshots__/button.test.js.snap`. This new folder/file is created for you after you run your tests.

## Conclusion
There you have it! These are the basics in React Unit Testing. Once you've got these fundamental concepts down, you can continue to explore more complex testing. Additionally, you can use tests to create labs for anyone you end up mentoring in your longterm career. 

If you'd like access to the repo for this tutorial, [you can find it here](https://github.com/MatthewPalmer9/react-unit-testing)

Happy coding!