Beginner Guide on Unit Testing in React using React Testing Library and Vitest
Tags: webdev, beginners, react, testing
Published: 2022-12-18 20:52:23


As Application grows bigger and more complex, manual testing becomes time-consuming, difficult to achieve its purpose and prone to error as it might be difficult to notice the wee change that can break the functionalities. In this article, I'd like to explain the concept of unit testing, I expect you have a basic understanding of `React` concepts like `components` and `state`.

lets get started💧

### Table Of Content
* [What is Unit Testing ?](#what-is-unit-testing) 
* [Purpose Of Unit Testing](#purpose-of-unit-testing) 
* [What is React Testing Library ?](#what-is-react-testing-library) 
* [Setting React App With Vite](#setting-react-app-with-vite) 
* [Vitest](#vitest) 
  * [Why use Vitest?](#why-use-vitest) 
  * [Stages of Testing](#stages-of-testing) 
  * [Write Your First Unit Test](#write-your-first-unit-test) 


### What is Unit Testing?

Unit tests are tests focused on individual `component`, function modules called `unit`. It isolates the particular unit and `test` it separately to ensure it works as expected. For `React` `components`, it could mean checking if the `component` renders correctly for a specified prop.

> The more your tests resemble the way your software is used, the more confidence they can give you.


### Purpose Of Unit Testing

1. **Improved code quality and reliability**: Unit testing ensures that individual components and functions in a `React` application are working as intended, reducing the chances of bugs and errors.

2. **Easier debugging and maintenance**: With unit tests in place, it is easier to identify and fix any code issues and make modifications and updates without breaking the application.

3. **Better collaboration and communication among team members**: Unit tests provide clear documentation of each `component's` expected behavior and functionality, enabling team members to understand and work on the code more effectively.

4. **Enhanced user experience**: By thoroughly testing each `component` and `function`, `unit tests` ensure that the user experience is consistent and seamless, resulting in a better overall product.

5. **Faster development and deployment**: With the ability to quickly identify and fix issues, unit testing allows for faster development and deployment of `React` applications.

### What is React Testing Library?

[**React Testing Library**](https://testing-library.com) `RTL` is a lightweight solution for testing web pages by querying and interacting with DOM nodes.
`RTL` is a popular testing library for `React` applications that helps developers write reliable tests for their components. It is designed to encourage writing tests that are easy to read and understand, which makes it a great tool for teams working on large React projects.

One of the key features of `react-testing-library` is its focus on testing the behavior of a `component` rather than its implementation. This means that tests written with `react-testing-library` are less likely to break when the implementation of a `component` changes, which can save developers a lot of time and effort.
The library provides a set of utility functions for interacting with `React components` in a way that simulates how a user would interact with them. 

### Setting React App With Vite

To set up our application,  we will be using `vite` as an alternative to `create-react-app`, `Vite` is a lightweight and minimalistic tool, providing only the essential features and tools needed for `React` development.

To create a `vite` project, run the following command in your terminal

```
npm create vite@latest

OR

yarn create vite

```

Then follow the prompts to  input project name, framework and variant:

* For project name, we can use `react-testing-project`
* For framework, select `React` option
* For `variant` select `javascript` from the available options

After All these, run the following

```

cd react-testing-project
npm install

OR

cd react-testing-project
yarn install

```

After these steps, open it in your favorite `IDE` or run
`code . ` to open in default `IDE` and start the server with the code below

```
npm run dev

OR

yarn run dev

```

Open the URL in your browser and it should display something similar here

![image of VS Code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0dtpf38l6ug1llxqg8tn.png)

### Vitest

`Vitest` is a blazing-fast unit test framework powered by `vite`. It aims to position itself as the rest runner of choice for `vite` projects, and as a solid alternative even for projects not using `vite`.

### Why use Vitest?

* Shared configuration with `vite`.
* `Vitest` supports `HMR` (Hot Module Reloading), which speeds up your workflow. With `HMR`, only the changes are updated on the server, and the server reflects the new changes.
* `Vitest` is relatively simple to use and doesn't require complex configuration. This makes it easier to get started with testing your components.
* `Jest` Snapshot support.

### Stages of Testing

* **Setup**: Before you can test an element, you will need to set up your testing environment. This typically involves installing the testing library and any dependencies, creating a test file, and importing the necessary components and utilities from the library.

* **Rendering**: To test an element, you will need to render it in a testing environment. You can use the render function from the testing library to do this, which will return an object containing the rendered element and various utility functions for interacting with it.

* **Querying**: Once you have rendered the element, you can use the query functions provided by the testing library (such as `getByText` or `getByLabelText`) to locate specific elements within the rendered tree.

* **Interacting**: After you have located the element you want to test, you can use the utility functions provided by the testing library (such as `fireEvent` or `userEvent`) to simulate user interactions with the element.

* **Assertion**: Finally, you can use the assertion functions provided by the testing library (such as expect or assert) to verify that the element is behaving as expected in response to the simulated interactions.



 * **Setup**

Install `vitest` and `jsdom` as dev dependencies using:

```

npm i -D vitest jsdom

OR

yarn add -D vitest jsdom

```


Also the following also

```
npm i @testing-library/jest-dom @testing-library/react

OR

yarn add @testing-library/jest-dom @testing-library/react

```


After all installations are successful, we need to add a test script in our `package.json` file like this:

```
test: "vitest"

```

So, our `package.json` file, look like this


![Package.json file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qccpan7qnqbhj6s5vqaz.png)

In `src` folder, create a `setupTests.js` and  add the following code

`import "@testing-library/jest-dom/extend-expect`

Also in our `vite.config.js` add a test object to the config like below

```

 test: {
    globals: true,
    environment: "jsdom",
    setupFiles: "src/setupTests.js",
  },

```

so, our `vite.config.js` looks like this:

![vite.config.js file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0qe7m60g1j3cnlgn572h.png)


We are done with installation and configuration, last thing left before running our test is to create `__tests__` folder in `src` folder. we will also create `App.test.js` in `__tests_` folder where we are to write all our codes, our file structure should look like this


![file layout](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/alywr5zitzjv43gsi0f5.png)

* **Querying**

Before writing our test, we should get acquainted with several methods that allow us to locate elements based on their text content, display properties, or other attributes on the page. 

* To select a single element, you can use the `getBy*`, `findBy*`, or `queryBy*`.

* To select multiple  elements, you can use the `getAllBy*`, `findAllBy*` or `queryAllBy*`.

* `queryBy*`: These element selectors allow you to search for an element in your component, but don't expect the element to be present. They will return the element if it is found or return null if the element is not found. These element selectors are useful when you want to check for the presence of an element, but don't want the query to throw an error if the element is not found.

* `getBy*`: These element selectors allow you to synchronously search for an element in your component. They will return the element if it is found or throw an error if the element is not found. These element selectors are useful when you expect the element to be present in the DOM at the time the query is executed.

* findBy*: These element selectors allow you to asynchronously search for an element in your component. They will return a promise that resolves to the element when it becomes available or rejects it with an error if the element is not found. These element selectors are useful when you need to wait for an element to appear in the DOM before interacting with it.

* `queryAllBy*`: These element selectors allow you to search for multiple elements in your component, but don't expect the elements to be present. They will return an array of elements if any elements are found, or return an empty array if no elements are found. These element selectors are useful when you want to check for the presence of multiple elements, but don't want the query to throw an error if no elements are found.

* `getAllBy*`: These element selectors allow you to synchronously search for multiple elements in your component. They will return an array of elements if any elements are found, or throw an error if no elements are found. These element selectors are useful when you expect multiple elements to be present in the DOM at the time the query is executed.

* `findAllBy*`: These element selectors allow you to asynchronously search for multiple elements in your component. They will return a promise that resolves to an array of elements when the elements become available, or rejects with an error if no elements are found. These element selectors are useful when you need to wait for multiple elements to appear in the DOM before interacting with them.

Read more about the configuration  [here](https://testing-library.com/docs/react-testing-library/intro/)  

### Write Your First Unit Test

In Our `App.jsx` file, we have some code there that we can write a basic test to debug our screen and check if some elements are present , 

* **Rendering**

 lets debug our screen, copy and paste the code below


```
import App from "../App";
import { it, describe } from "vitest";
import { render, screen } from "@testing-library/react";


describe("App.js", () => {
  it("Check if the App render very well", () => {
    //render our App properly
    render(<App />);
    screen.debug();
  });
});

```

Here, we import our `<App/>` which is to be tested, Also screen and render are imported to be able to interact with the component. `describe` method is used to organize and structure tests, and it typically takes a string parameter that describes the group of tests being defined and a `callback function` that contains the actual tests. In this example, the string argument, "Check if the App render very well", provides a label for the group of tests, and the `callback function` contains the actual tests that are being run. The `render` method is used to render a given `React` component and return an object that provides several utility functions for interacting with the rendered component. `screen.debug` method is a utility function that allows you to print the current state of the rendered component to the `console`. This can be useful when writing or debugging tests.

lets run the test by entering the following code

```
npm run test

OR

yarn run test

```
After running the test, it renders our `<App/>` in the terminal as shown below

![result of test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8mw0fbjxkkwtbqcmlnq.png)

* **Assertion**
lets check if `button` and `heading` elements are in our component will the following code:

```
import App from "../App";
import { it, describe } from "vitest";
import { render, screen } from "@testing-library/react";

describe("App.js", () => {
  it("Check if the button is in the document", () => {
    render(<App />);
    const button = screen.getByRole("button");
    expect(button).toBeInTheDocument();
  });

  it("check if h1 element is in the document", () => {
    render(<App />);
    const h1 = screen.getByRole("heading", { level: 1 });
    expect(h1).toBeInTheDocument();
  });
});

```

Here, we are having two tests that render our `<App/>` , in `App.jsx` , I added an `attribute` of `role` with the value of `button` to easily find the element, `getByRole` method accepts a second parameter which we used to indicate the heading is `h1`. `expect` is used to define the expected behaviour of a test and to verify that the actual behaviour matches the expected behaviour. in our two tests, we expect both elements to be in the document. below is what happens when we  run our test 


![expected result on test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qp47bawrr5zk494dlj7d.png)

* **Interaction** 
In this test, we will test when a user interact with element such as `click` event and check the `state` change as shown below

```

import App from "../App";
import { it, describe,  } from "vitest";
import { render, screen } from "@testing-library/react";

describe("App.js", () => {
  it("Check if the button is in the document", () => {
    render(<App />);
    const button = screen.getByRole("button");
    expect(button).toBeInTheDocument();
  });

  it("check if h1 element is in the document", () => {
    render(<App />);
    const h1 = screen.getByRole("heading", { level: 1 });
    expect(h1).toBeInTheDocument();
  });
  it("Check if the current value renders when click", () => {
    render(<App />);
    const h1 = screen.getByRole("heading", { level: 1 });
    expect(h1.textContent).toBe("0");
  });
});

```
since we render `App.jsx` in the three test, we can refactor our code and keep it `DRY` by using another method `beforeEach`, which runs before each test, so our updated test code becomes

```
import App from "../App";
import { it, describe, beforeEach } from "vitest";
import { render, screen } from "@testing-library/react";

beforeEach(() => {
  render(<App />);
});

describe("App.js", () => {
  it("Check if the button is in the document", () => {
    const button = screen.getByRole("button");
    expect(button).toBeInTheDocument();
  });

  it("check if h1 element is in the document", () => {
    const h1 = screen.getByRole("heading", { level: 1 });
    expect(h1).toBeInTheDocument();
  });
  it("Check if the current value renders when click", () => {
    const h1 = screen.getByRole("heading", { level: 1 });
    expect(h1.textContent).toBe("0");
  });
});

``` 
With this, Our code becomes neater and more readable, our test is expected to run successfully.

I made a change to `App.jsx` file, by putting our state in the `h1` element. as shown below

![updated App.js file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7bk5psykjhv58hnr17ai.png)

To simulate the click event, I added the following code

```
it("Check if the  value increases when button is clicked", async () => {
    const button = screen.getByRole("button");
    await userEvent.click(button);
    const h1 = screen.queryByRole("heading", { level: 1 });
    expect(h1.textContent).toBe("4");
  });

```
we use  `async` and `await` when testing asynchronous events such as `click`, because it ensures that the test will wait for the `click` event to complete before moving on to the next step. running this test will definitely fail because the expected value when clicked is `1` instead of `4`

![failed test result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1t7mt36bu7mfmchbxw23.png)

So if we change the value from `4` to `1`, the test runs as expected as shown below

![successfully test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dciv0mfbogjnq28sne04.png)

Thank you for reading. I hope you've learned something new from this post. The code can be found in this [repository](https://github.com/isiakaabd/react-unit-tests-example) Want to stay up to date with regular content regarding JavaScript, React? Follow me on  [LinkedIn](https://linkedin.com/in/isiakaabd).