DEV Community

Sheikh Limon
Sheikh Limon

Posted on

Setting Up Vitest + React Testing Library

Let's configure a modern, professional testing environment for React โ€” the same stack used by industry teams.

๐ŸŽฏ What We're Building

This setup combines:

  • Vite โ†’ Lightning-fast dev server and build tool
  • Vitest โ†’ Modern, Jest-compatible test runner with native ESM support
  • React Testing Library โ†’ User-centric UI testing that mimics real interactions
  • @testing-library/jest-dom โ†’ Readable, semantic assertions
  • jsdom โ†’ Lightweight browser environment for Node.js

๐Ÿชœ Step 1: Install Dependencies

Run this in your project root:

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

What -D means: These are devDependencies โ€” they're only needed during development, not in production.

๐Ÿชœ Step 2: Configure Vitest

Create or update vite.config.ts in your project root:

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

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true, // Enables 'test', 'expect' globally (no imports needed)
    environment: "jsdom", // Simulates browser DOM for React components
    setupFiles: "./src/setupTests.ts", // Runs before each test file
  },
});
Enter fullscreen mode Exit fullscreen mode

Why this matters:

  • globals: true โ†’ Write tests without repetitive imports
  • environment: "jsdom" โ†’ Your components render as if in a real browser
  • setupFiles โ†’ Configure test helpers once, use everywhere

๐Ÿชœ Step 3: Create Test Setup File

Create src/setupTests.ts:

import "@testing-library/jest-dom";
Enter fullscreen mode Exit fullscreen mode

What this unlocks:

Now you can use human-readable assertions like:

expect(element).toBeInTheDocument();
expect(button).toBeDisabled();
expect(link).toHaveAttribute("href", "/home");
expect(heading).toHaveTextContent("Welcome");
Enter fullscreen mode Exit fullscreen mode

Instead of verbose, fragile alternatives like:

// โŒ Without jest-dom
expect(element).toBeTruthy();
expect(button.disabled).toBe(true);
Enter fullscreen mode Exit fullscreen mode

๐Ÿชœ Step 4: Configure TypeScript

Update tsconfig.json to recognize Vitest and the DOM environment:

{
  "compilerOptions": {
    "types": ["vitest/globals", "vite/client", "@testing-library/jest-dom"],
    "jsx": "react-jsx",
    "module": "ESNext",
    "target": "ES2022",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}
Enter fullscreen mode Exit fullscreen mode

Key additions:

  • "vitest/globals" โ†’ TypeScript recognizes test, expect, describe
  • "@testing-library/jest-dom" โ†’ Autocomplete for custom matchers

๐Ÿชœ Step 5: Configure ESLint for Testing

Install the ESLint plugins for React Testing Library and jest-dom:

npm install -D eslint-plugin-testing-library eslint-plugin-jest-dom
Enter fullscreen mode Exit fullscreen mode

Update your eslint.config.js (ESLint flat config):

import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";
import { defineConfig, globalIgnores } from "eslint/config";
import eslintConfigPrettier from "eslint-config-prettier";
import testingLibrary from "eslint-plugin-testing-library";
import jestDom from "eslint-plugin-jest-dom";

export default defineConfig([
  globalIgnores(["dist"]),
  {
    files: ["**/*.{ts,tsx}"],
    extends: [
      js.configs.recommended,
      tseslint.configs.recommended,
      reactHooks.configs["recommended-latest"],
      reactRefresh.configs.vite,
      eslintConfigPrettier,
    ],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
    },
  },
  // Testing Library + Jest DOM rules for test files only
  {
    files: [
      "**/__tests__/**/*.{js,jsx,ts,tsx}",
      "**/*.{test,spec}.{js,jsx,ts,tsx}",
    ],
    extends: [
      testingLibrary.configs["flat/react"],
      jestDom.configs["flat/recommended"],
    ],
  },
]);
Enter fullscreen mode Exit fullscreen mode

What this provides:

โœ… Best practice enforcement โ†’ Warns against anti-patterns like getByTestId

โœ… Async handling โ†’ Catches missing await on async queries

โœ… Accessible queries โ†’ Suggests using getByRole over less semantic queries

โœ… jest-dom matchers โ†’ Autocomplete and validation for custom assertions

Example linting feedback:

// โŒ ESLint will warn
const button = container.querySelector('button');

// โœ… ESLint suggests
const button = screen.getByRole('button', { name: /submit/i });
Enter fullscreen mode Exit fullscreen mode

๐Ÿชœ Step 6: Add Test Script

In package.json:

"scripts": {
  "dev": "vite",
  "build": "vite build",
  "test": "vitest",
  "test:ui": "vitest --ui" // optional UI runner
}
Enter fullscreen mode Exit fullscreen mode

Run tests with:

npm run test
Enter fullscreen mode Exit fullscreen mode

or start an interactive UI:

npm run test:ui
Enter fullscreen mode Exit fullscreen mode

๐Ÿง  Why This Setup Works

โœ… Vitest understands modern ES Modules (no CommonJS issues)

โœ… React Testing Library gives realistic user-focused queries

โœ… JSDOM simulates the browser for rendering

โœ… jest-dom improves readability of test results

โœ… setupTests.ts ensures global test behavior is consistent

โœ… Example: A Complete Test

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

test("user can add a task", async () => {
  render(<App />);

  const input = screen.getByRole("textbox", { name: /add task/i });
  const button = screen.getByRole("button", { name: /add/i });

  await userEvent.type(input, "Learn TDD");
  await userEvent.click(button);

  expect(screen.getByText("Learn TDD")).toBeInTheDocument();
});
Enter fullscreen mode Exit fullscreen mode

When you run it, Vitest uses:

  • JSDOM to render your app
  • Testing Library to simulate user behavior
  • jest-dom to verify output

๐Ÿ’ก Pro Tip

You can watch tests live as you code:

npx vitest --watch
Enter fullscreen mode Exit fullscreen mode

Vitest will re-run only the tests affected by the files you changed โ€” perfect for iterative TDD development.

๐Ÿ Final Thoughts

With this setup, you now have a professional-grade testing environment that mirrors what modern React teams use in production:

  • โšก Vite โ†’ fast bundling
  • ๐Ÿงช Vitest โ†’ Jest-compatible testing
  • ๐Ÿงโ€โ™‚๏ธ React Testing Library โ†’ test like a real user
  • ๐Ÿงฑ TypeScript โ†’ safe and typed components
  • ๐Ÿงญ TDD workflow โ†’ focused, confident coding

โœ… "Write tests for behavior, not for implementation."

๐ŸŽ“ Remember

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

โ€” Kent C. Dodds, creator of React Testing Library

With this setup, you're not just testing code โ€” you're verifying that real users can accomplish their goals.

Next steps: Start practicing TDD by writing a failing test first, then implementing just enough code to make it pass. ๐ŸŽฏ

Top comments (0)