Testing a React Application with Vitest and React Testing Library
Tags: webdev, react, testing, vite
Published: 2024-07-14 00:30:00

This guide covers how to set up testing for a React application using Vitest, focusing on building an accordion component and applying Test Driven Development (TDD) principles. We will be using default counter app provided by vite.

We are going to configure vitest and implement test cases. I will be using pnpm as package manager for this but you can use yarn or npm as well.
Here are some test cases we are going to write for App (Counter App) component using React Testing Library and Vitest:

1. Ensure the logos are rendered correctly with the correct links.
2. Ensure the initial count is displayed correctly.
3. Ensure the count increments when the button is clicked.
4. Ensure the text content is displayed correctly.

## Initializing the React Project
We can [initialize the vite project](https://vitejs.dev/guide/#scaffolding-your-first-vite-project) using the following commands and fill in project details:
```bash
## For pnpm:
pnpm create vite

# For NPM
npm create vite@latest 

## For yarn 
yarn create vite

cd <YOUR_PROJECT_NAME>
pnpm install
```
This should have your project setup. You can run project and access it locally.

## Adding dependencies for Testing:
Install Vitest, jsdom and @testing-library/react as a development dependencies:
```bash
pnpm install -D vitest jsdom @testing-library/react
``` 

## Updating Configurations:

### Setup `setupTests` file:
Create a test setup file at `setupTests.ts` in the `tests` folder of your project or in the src directory, whichever you prefer. Implement it as follows:
```typescript
import { afterEach } from 'vitest'
import { cleanup } from '@testing-library/react'
import '@testing-library/jest-dom/vitest'

afterEach(() => {
  cleanup();
})
```
### Updating `vite.config.ts`:
One advantage of Vitest is its use of the same configuration as Vite. This alignment ensures that the test environment mirrors the build environment, enhancing the reliability of the tests.

We will update the configuration to the following code to include js-dom, which facilitates testing:
```typescript
test: {
    globals: true,
    environment: 'jsdom',

    setupFiles: './src/tests/setupTests.ts',
  }
```
So final vite config will look something like this:
```typescript
/// <reference types="vitest" />
/// <reference types="vite/client" />

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

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

    setupFiles: './src/tests/setupTests.ts',
  },
})
```
## Add the Test Script:
We need to add the vitest command to the `package.json` to start the testing script.
```json
"scripts": {
    // ...rest of scripts
    "test": "vitest"
  },
```
once this is done, we can implement test cases for our component.
## Writing Tests for the Counter App:
Create `App.test.tsx` in tests folder with following contents:
```typescript
import { describe, it, expect } from 'vitest'
import { render, screen, fireEvent } from '@testing-library/react'
import App from '../App'

describe('App component', () => {
  it('renders Vite and React logos with correct links', () => {
    render(<App />)
    
    const viteLogo = screen.getByRole('img', { name: /Vite logo/i });
    const reactLogo = screen.getByAltText('React logo')

    expect(viteLogo).toBeInTheDocument()
    expect(reactLogo).toBeInTheDocument()

    expect(viteLogo.closest('a')).toHaveAttribute('href', 'https://vitejs.dev')
    expect(reactLogo.closest('a')).toHaveAttribute('href', 'https://react.dev')
  })

  it('renders the initial count correctly', () => {
    render(<App />)
    const button = screen.getByRole('button', { name: /count is 0/i })
    expect(button).toBeInTheDocument()
  })

  it('increments count when button is clicked', () => {
    render(<App />)
    const button = screen.getByRole('button', { name: /count is 0/i })
    fireEvent.click(button)
    expect(button).toHaveTextContent('count is 1')
  })

  it('renders text content correctly', () => {
    render(<App />)
    
    const title = screen.getByText('Vite \+ React')
    const editText = screen.getByText((content, _) => content.includes('Edit') && content.includes('save to test HMR'))
    const docsText = screen.getByText('Click on the Vite and React logos to learn more')
    
    expect(title).toBeInTheDocument()
    expect(editText).toBeInTheDocument()
    expect(docsText).toBeInTheDocument()
  })
})
```
## Running Tests:
You can run tests by running:
```bash
pnpm run test
```

From the code example provided, we have demonstrated how Vitest integrates seamlessly with the React Testing Library. As previously mentioned, these libraries work in harmony: Vitest facilitates the creation of test suites, test cases, and test execution, while the React Testing Library enables thorough testing of React components by simulating user interactions with our application.

You can access full project here:
[![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/manjushsh/vite-react-ts-vitest-template)