Testing a React application with Vitest
Tags: react, vite, vitest
Published: 2022-07-23 10:17:13

---
title: Testing a React application with Vitest
published: true
date: 
tags: react, vite, vitest
canonical_url: https://eternaldev.com/blog/testing-a-react-application-with-vitest
cover_image: https://www.eternaldev.com/static/5ff5dc99f0574e1e546c234b85d43b17/aa1b3/reactvitevitestposter.webp
---

React is really popular when creating frontend components and when the scale of your application is increased, we need to have robust tests to cover all scenarios. The unit testing component becomes a great way to ensure the quality of the application and easily find bugs in the build time when the tests are written well. In this post, we will learn how to test a component with React and Vitest

## What are we building?

We are going to build a simple accordion component in React and write unit tests in Vitest. The component will have two states. First is the collapsed state which displays only the title. Another state will be the open state which shows the title and the content below the title.

[![AccordionFinal.png](https://eternaldev.com/static/f14d1788bba62852226ee06c95ec94bb/c72f7/accordionfinal.png "React accordion example")](/static/f14d1788bba62852226ee06c95ec94bb/c72f7/accordionfinal.png)

## What is Vite?

Vite is a build tool that is easy to use and fast to compile the react project. We will make use of vite since it is easy to integrate the Vitest testing tool. If you want to know the basics of Vite, we have covered it in this [blog post](https://www.eternaldev.com/blog/build-and-deploy-apps-with-svelte-and-vite/)

## Why use vitest?

Vitest is really fast and has a good developer experience when used with Vite. We can share the configuration of vite with vitest to make it simple and also ensures that the testing environment is similar to the build environment. Vitest supports HMR which really speeds up your workflow

## What is HMR?

HMR stands for Hot Module Reloading. Whenever there is any change to the code, only the changes are updated to the server and the server reflects the new changes

The speed of seeing the changes on your server is improved as we are only sending partial changes for reloading instead of reloading the whole code.

Now that all the jargons are out of the way, let’s see some code that you have come for.

## Initialize React project using Vite

We can initialize the project using the following command

```bash
npm init vite
cd react-vite-vitest
npm install
```

[![ProjectInit.png](https://eternaldev.com/static/5fdc6e0c5ebc339fe50b5f98719f269b/1b1d5/reactviteprojectinit.png "React project init with vite")](/static/5fdc6e0c5ebc339fe50b5f98719f269b/1b1d5/reactviteprojectinit.png)

## Adding Vitest for Testing

We can add Vitest to start adding tests to the project. Install the Vitest as a dev dependency.

```bash
npm install -D vitest
```

## Vitest Configuration

One of the advantages of Vitest is that it uses the same config as Vite. This ensures that the test environment is the same as the build environment which increases the reliability of the tests

We will update the config to the following code to add `js-dom` which helps in testing

```js
/// <reference types="vitest" />
/// <reference types="vite/client" />

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
    test: {
      globals: true,
      environment: 'jsdom',
    }
})
```

## Creating an Accordion component

Create a new component called `Accordion.tsx` and add the following code to create a simple accordion component. It is still incomplete and we will complete it by adding testing first

```jsx
import React from "react";

type AccordionProps = {
    title: string;
    children: React.ReactNode;
}
const Accordion = (props: AccordionProps) => {

    const {title, children} = props;

    return (
        <div className="accordion">
            <h3 className="accordion-title">{title}</h3>
            <div className="accordion-content">
                {children}
            </div>
        </div>
    );
}

export default Accordion;
```

[![ReactAccordionStart1.png](https://eternaldev.com/static/ce58eadfcf7636bd10a21d479154ca48/7ebf9/reactaccordionstart1.png "React accordion example")](/static/ce58eadfcf7636bd10a21d479154ca48/7ebf9/reactaccordionstart1.png)

We are just taking the `title` and `children` and displaying them. An accordion component should be able to shrink and expand when a button is clicked. So let’s add a test case first for that feature and then implement it.

## Creating the test in vitest

Create a new file called `Accordion.test.tsx` which will contain the test for the Accordion component. Add the following code to that file

```jsx
import {describe, test} from 'vitest';

describe("Accordion test", () => {
    test("Should show title", () => {

    })
})
```

Let’s break down the above code

1. describe - Used to group the test and used to describe what is currently being tested
2. test - Individual test which is run by Vitest. It can either pass or fail

Here we have not added any test which will return true or false. We will do that shortly after adding the test script

### Adding the test script

We need to add the `vitest` command to the package.json to start the testing script.

```bash
"scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "test": "vitest"
  },
```

Just by calling the `vitest` command, the test execution will start and it will be in `watch` mode. This means that any changes being done to the file will re-run the test.

### Start the test script

```bash
npm run test
```

[![InitialTest.png](https://eternaldev.com/static/1b5bba015ea3ce6a9d813b3057f1cb2a/0f2bc/reactviteinitialtest.png "React vite test case")](/static/1b5bba015ea3ce6a9d813b3057f1cb2a/0f2bc/reactviteinitialtest.png)

Since we don't have any expect statement, the test is considered to be passed

## Adding the config for Vitest

We need to have the DOM functionality replicated in the test environment to properly test the react components

JSDom helps in getting that environment for test and so we need to install that as a dev dependency.

We will also make use of `testing-library` as well which will help in having more utility functions to help test the components. We will get things like `render` function from this package which will simulate the component being rendered on the browser.

### Installing the testing dependecies

```bash
npm i -D jsdom @testing-library/react
```

### Adding the config for Vitest

```jsx
/// <reference types="vitest" />
/// <reference types="vite/client" />

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
    test: {
      globals: true,
      environment: 'jsdom',
    }
})
```

## Write a unit test to see if text is visible in vitest

```jsx
import {describe, expect, test} from 'vitest';
import {render, screen} from '@testing-library/react';
import Accordion from './Accordion';

describe("Accordion test", () => {
    test("should show title all the time", () => {
        
        render(<Accordion title='Testing'><h4>Content</h4></Accordion>);

        expect(screen.getByText(/Testing/i)).toBeDefined()
    })
})
```

This is the first basic test that makes sure that the title is always displayed on the screen. We are making use of some of the functions from `testing-library` like `render` and `screen.getByText`

getByText returns the element if found otherwise it will throw an exception which will fail the test case.

There are lot more utility functions to choose from based on your use case

[https://testing-library.com/docs/react-testing-library/api](https://testing-library.com/docs/react-testing-library/api)

## Create a test for hiding and showing content

We need to render the component on each test case. We can make use of `beforeEach` in this case which will run the code inside before each test

```jsx
import {beforeEach, describe, expect, test} from 'vitest';
import {render, screen} from '@testing-library/react';
import Accordion from './Accordion';

describe("Accordion", () => {

    beforeEach(() => {
        render(<Accordion title='Testing'><h4>Content</h4></Accordion>);
    });

    test("should show title all the time", () => {
       
        expect(screen.getByText(/Testing/i)).toBeDefined()
    })

    test("should not show the content at the start", () => {

        expect(screen.getByText(/Content/i)).toBeUndefined()

    })
})
```

[![ContentNotShownTestFail.png](https://eternaldev.com/static/c15a8f1bb174ea3fe735fdc1fbb8fa83/c8e86/contentnotshowntestfail.png "React vite test fail example")](/static/c15a8f1bb174ea3fe735fdc1fbb8fa83/c8e86/contentnotshowntestfail.png)

The second test should be failing now because we are expecting that the content should not be shown at the start but we didn’t implement the code to do that. This is a good example of how TDD (Test Driven Development) works. We first write a test that will fail and then implement the functionality to make it pass.

## Implementing the logic to pass the test

```jsx
import React, { useState } from "react";
import './Accordion.css'

type AccordionProps = {
    title: string;
    children: React.ReactNode;
}
const Accordion = (props: AccordionProps) => {

    const {title, children} = props;
    const [show, setShow] = useState(false);

    const onAccordionClick = () => {
        setShow(!show);
    }

    return (
        <div className="accordion">
            <div className="accordion-title">
                <h3>{title}</h3>
                <button onClick={() => onAccordionClick()}>{!show ? 'Show' : 'Hide'}</button>
            </div>
            {show && (
                <div>
                    {children}
                </div>
            )}
        </div>
    );
}

export default Accordion;
```

We are adding the code to hide and show the content of the accordion. This is achieved by simply changing the state variable of `show`

We are setting the initial value of `show` to false which will make the test pass.

Now that we have the basic accordion feature completed, let’s focus on getting more styles using CSS.

### Adding the styles for the Accordion

```css
.accordion {
    width: 80vw;
    border: 1px solid gray;
    border-radius: 5px;
}

.accordion-title {
    padding: 0px 25px;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    border-bottom: 1px solid gray;
}
```

## Writing test to validate the open/close behavior

We have completed the functionality of Accordion. Now we can add one more test to see if the accordion opens with a click of the button.

Let’s write the 3rd test like below

`fireEvent` from the testing library helps in simulating user actions in a unit test. We are using the `click` method to click the button. This should trigger the accordion to open and then we are awaiting the action to take place. Since that will be async action, we are using the `await` keyword.

The async unit test will have a default timeout and it will wait till that time. Once the timeout is completed, it will fail the test.

```jsx
import {beforeEach, describe, expect, test} from 'vitest';
import {fireEvent, render, screen, waitFor} from '@testing-library/react';
import Accordion from './Accordion';
import "@testing-library/jest-dom";
import { act } from 'react-dom/test-utils';

describe("Accordion", () => {

    beforeEach(() => {
        render(<Accordion title='Testing'><h4>Content</h4></Accordion>);
    });

    test("should show title all the time", () => {
       
        expect(screen.getByText(/Testing/i)).toBeInTheDocument();
    })

    test("should not show the content at the start", () => {

        expect(screen.queryByText(/Content/i)).not.toBeInTheDocument();
    })

    test("should show the content on accordion click",async () => {

        const title = screen.getByText(/Show/i);
        fireEvent.click(title)

        expect(await screen.findByText(/Content/i)).toBeInTheDocument();
    })
})
```

[![AlltestPass.png](https://eternaldev.com/static/ffad494bc76860260e9c5d874f966928/5b481/reactvitealltestpass.png "React vite all test passed example")](/static/ffad494bc76860260e9c5d874f966928/5b481/reactvitealltestpass.png)

## Conclusion

We have learned how to write unit tests with Vitest in React. Vitest is still in the beta stage and not yet ready for production use. We think vitest has a huge potential and looks like a good alternative to Jest which is being used by a lot of developers.

Let us know if you have worked with Vitest and any feedback on this post is welcome

Join our Discord - [https://discord.gg/AUjrcK6eep](https://discord.gg/AUjrcK6eep)