DEV Community

Cover image for React Testing Setup: Vitest + TypeScript + React Testing Library

React Testing Setup: Vitest + TypeScript + React Testing Library

In this post, I’ll walk you through how to set up a React app using Vite and TypeScript with Vitest and React Testing Library.

This kind of setup isn’t included by default in Vite, and it can be a bit tedious the first time you try it. So, here’s a quick guide to help you get everything running smoothly.

If you prefer to skip the explanations and just want the final result, you can jump to the summary section at the end, where I list all the required installations and final file versions.


1. Create a New React + TypeScript Project

Start by creating a new Vite app:

npm create vite@latest
Enter fullscreen mode Exit fullscreen mode

When prompted, name the project:

react-testing-configuration-example
Enter fullscreen mode Exit fullscreen mode

Then choose:

  • Framework: react
  • Variant: TypeScript

terminal with commands

Now install dependencies and start the dev server:

cd react-testing-configuration-example
npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

You should see the default React app running at http://localhost:5173/.

basic react app

2. Add Vitest

Install Vitest as a dev dependency:

npm install -D vitest
Enter fullscreen mode Exit fullscreen mode

Then update your package.json scripts:

"scripts": {
  "dev": "vite",
  "build": "tsc -b && vite build",
  "lint": "eslint .",
  "test": "vitest",
  "preview": "vite preview"
},
Enter fullscreen mode Exit fullscreen mode

Create your first test file in src/tests/app.spec.ts. The filename must include .spec or .test for Vitest to recognize it:

import { describe, it, expect } from 'vitest';

describe('Simple truthy test', () => {
  it('should expect true to equal true', () => {
    expect(true).toEqual(true);
  });
});
Enter fullscreen mode Exit fullscreen mode

Run the tests:

npm test
Enter fullscreen mode Exit fullscreen mode

success tests

3. Enable Global Test Functions

Usually, you don’t want to import describe, it, and expect in every file.

To enable global test functions, update your vite.config.ts:

/// <reference types="vitest" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
  },
})
Enter fullscreen mode Exit fullscreen mode

Then make sure TypeScript knows about the global types.

In your tsconfig.app.json, add:

{
  "compilerOptions": {
    "types": ["vitest/globals"],
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true,
    "moduleDetection": "force",
    "noEmit": true,
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "erasableSyntaxOnly": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true
  },
  "include": ["src"]
}
Enter fullscreen mode Exit fullscreen mode

This removes the TypeScript errors for global functions like describe.

4. Add React Testing Library

Install the testing libraries:

npm install --save-dev @testing-library/react @testing-library/dom @types/react @types/react-dom
Enter fullscreen mode Exit fullscreen mode

Rename your test file to app.spec.tsx (so JSX is supported), and update it:

import { render, screen } from '@testing-library/react';
import App from '../App';

describe('App component', () => {
  it('renders the Vite + React heading', () => {
    render(<App />);
    expect(screen.getByText(/Vite \+ React/i)).toBeInTheDocument();
  });
});
Enter fullscreen mode Exit fullscreen mode

If you run the tests now, you’ll likely get an error related to the DOM environment:

Error tests

Install jsdom to fix that:

npm i --save-dev jsdom
Enter fullscreen mode Exit fullscreen mode

Update your vite.config.ts:

/// <reference types="vitest" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: "jsdom",
  },
})
Enter fullscreen mode Exit fullscreen mode

5. Fix toBeInTheDocument Assertion Error

Install jest-dom:

npm install --save-dev @testing-library/jest-dom
Enter fullscreen mode Exit fullscreen mode

Create a global test setup file at src/tests/setup.ts:

import { afterEach } from 'vitest'
import { cleanup } from '@testing-library/react'
import '@testing-library/jest-dom';

// runs a clean after each test case (e.g. clearing jsdom)
afterEach(() => {
  cleanup();
});
Enter fullscreen mode Exit fullscreen mode

And update your vite.config.ts again:

/// <reference types="vitest" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    setupFiles: "./src/tests/setup.ts",
    environment: 'jsdom'
  },
})
Enter fullscreen mode Exit fullscreen mode

Now your RTL assertions like toBeInTheDocument will work perfectly.

tests completados

6. Bonus: Testing User Events

Install user-event:

npm install --save-dev @testing-library/user-event
Enter fullscreen mode Exit fullscreen mode

Update your test file to include a counter interaction:

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import App from '../App';

describe('App component', () => {
  it('renders the Vite + React heading', () => {
    render(<App />);
    expect(screen.getByText(/Vite \+ React/i)).toBeInTheDocument();
  });

  it('increments the counter when button is clicked', async () => {
    render(<App />);
    const button = screen.getByRole('button', { name: /count is/i });
    expect(button).toHaveTextContent('count is 0');
    await userEvent.click(button);
    expect(button).toHaveTextContent('count is 1');
  });
});
Enter fullscreen mode Exit fullscreen mode

Now you’re ready to write modern, robust tests in your React + Vite + TypeScript apps!


Final Summary

Here’s a full list of commands you can run to install everything in one go:

npm install -D vitest jsdom @testing-library/react @testing-library/dom @types/react @types/react-dom @testing-library/jest-dom @testing-library/user-event
Enter fullscreen mode Exit fullscreen mode

vite.config.ts (Final)

/// <reference types="vitest" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    setupFiles: './src/tests/setup.ts',
    environment: 'jsdom',
  },
})
Enter fullscreen mode Exit fullscreen mode

tsconfig.app.json (Update)

{
  "compilerOptions": {
    "types": ["vitest/globals"]
    // ...rest of your config
  }
}
Enter fullscreen mode Exit fullscreen mode

src/tests/setup.ts

import { afterEach } from 'vitest';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom';

afterEach(() => {
  cleanup();
});
Enter fullscreen mode Exit fullscreen mode

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.