Skip to content

gnanaseelan93/React-Query-APP

Repository files navigation

Test Setup (Vitest) — React 19+ (Vite)

This README explains how to set up unit testing for a React 19+ app using Vite + Vitest + Testing Library. It includes packages to install, configuration, example tests (user-event), coverage configuration, Test UI, useful scripts, and troubleshooting.


✅ Prerequisites

  • Node.js (v16+ recommended)
  • npm (or yarn/pnpm)
  • Vite project created (React + Vite)

1) Install packages

Run this in your project root:

npm install --save-dev vitest @vitest/ui @vitest/coverage-v8 jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event

What each does:

  • vitest - test runner
  • @vitest/ui - optional web UI for Vitest
  • @vitest/coverage-v8 - V8-based coverage provider (fast)
  • jsdom - DOM environment for tests
  • @testing-library/react - render + query helpers
  • @testing-library/jest-dom - extra expect matchers
  • @testing-library/user-event - higher-level, realistic user interactions

2) package.json scripts

Add these to your package.json scripts section:

"scripts": {
  "dev": "vite",
  "build": "vite build",
  "lint": "eslint .",
  "preview": "vite preview",
  "test": "vitest",
  "test:ui": "vitest --ui",
  "test:watch": "vitest --watch",
  "test:coverage": "vitest run --coverage"
}
  • npm run test — run tests in terminal (interactive)
  • npm run test:ui — open web UI (interactive browser)
  • npm run test:watch — watch mode
  • npm run test:coverage — run tests and generate coverage reports

3) Vitest configuration

Create vitest.config.js (or vite.config.js if you prefer) in project root. Example using vitest/config:

// vitest.config.js
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,            // use global test APIs (describe/it/expect)
    environment: 'jsdom',     // simulate browser DOM
    setupFiles: './src/setupTests.js',
    coverage: {
      provider: 'v8',         // 'v8' via @vitest/coverage-v8
      reportsDirectory: './coverage',
      reporter: ['text', 'html'],
      all: true,
      include: ['src/**/*.{js,jsx,ts,tsx}']
    }
  }
})

If you already have vite.config.js, you can add a test field to it instead of creating a separate file.


4) Setup file for jest-dom

Create src/setupTests.js and add:

import '@testing-library/jest-dom'

This registers extra matchers like toBeInTheDocument().


5) Recommended folder structure

src/
  components/
    TodoForm.jsx
    __tests__/
      TodoForm.test.jsx
  context/
    TodoContext.jsx
  setupTests.js
vite.config.js (or vitest.config.js)
package.json

6) Example tests

A) user-event (recommended)

// src/components/__tests__/TodoForm.test.jsx
import { describe, it, expect, vi } from 'vitest'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import TodoForm from '../TodoForm'
import { TodoContext } from '../../context/TodoContext'

describe('TodoForm Component (userEvent)', () => {
  it('should submit the form and call addTodo', async () => {
    const addTodo = vi.fn()
    const user = userEvent.setup()

    render(
      <TodoContext.Provider value={{ addTodo }}>
        <TodoForm />
      </TodoContext.Provider>
    )

    const titleInput = screen.getByPlaceholderText(/Enter title/i)
    const statusSelect = screen.getByRole('combobox')
    const submitButton = screen.getByRole('button', { name: /add/i })

    await user.type(titleInput, 'Test Todo')
    await user.selectOptions(statusSelect, 'Pending')
    await user.click(submitButton)

    expect(addTodo).toHaveBeenCalledTimes(1)
    const arg = addTodo.mock.calls[0][0]
    expect(arg).toEqual(expect.objectContaining({ title: 'Test Todo', status: 'Pending' }))
    expect(typeof arg.id).toBe('number')

    expect(titleInput.value).toBe('')
    expect(statusSelect.value).toBe('Todo')
  })
})

B) Validation case (title required)

it('should not call addTodo when title is empty', async () => {
  const addTodo = vi.fn()
  const user = userEvent.setup()

  render(
    <TodoContext.Provider value={{ addTodo }}>
      <TodoForm />
    </TodoContext.Provider>
  )

  const submitButton = screen.getByRole('button', { name: /add/i })
  await user.click(submitButton)
  expect(addTodo).not.toHaveBeenCalled()
})

7) Run tests & coverage

  • Run tests (terminal):
npm run test
  • Run tests in watch mode:
npm run test:watch
  • Open Vitest UI:
npm run test:ui
  • Run tests + coverage and generate reports:
npm run test:coverage

After coverage run, open coverage/index.html in your browser (open the file directly or serve the folder).


8) Common troubleshooting

  • ReferenceError: window is not defined — ensure environment: 'jsdom' in config.
  • No coverage provider configured — make sure @vitest/coverage-v8 is installed and coverage.provider set to v8 (or use istanbul).
  • Tests failing due to missing setup — ensure src/setupTests.js is configured and setupFiles points to it.
  • Tests timing out or flaky — prefer userEvent.setup() and await on slow interactions.

9) Tips & best practices

  • Prefer user-event over fireEvent for realistic interactions.
  • Use AAA (Arrange, Act, Assert) structure in tests.
  • Mock external network calls (fetch/axios) using vi.stubGlobal or vi.mock.
  • Keep tests focused and deterministic.

10) Example package.json (minimal)

{
  "name": "my-app",
  "version": "1.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "test": "vitest",
    "test:ui": "vitest --ui",
    "test:coverage": "vitest run --coverage"
  },
  "devDependencies": {
    "vitest": "^",
    "@vitest/ui": "^",
    "@vitest/coverage-v8": "^",
    "jsdom": "^",
    "@testing-library/react": "^",
    "@testing-library/jest-dom": "^",
    "@testing-library/user-event": "^"
  }
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published