# Vitest Testing Guide for TypeScript and React ## Summary Vitest is a blazing fast unit testing framework powered by Vite, designed specifically for modern web development. It provides native support for TypeScript, ES modules, and React Testing Library, making it an excellent choice for testing React applications with TypeScript. This guide covers setup, configuration, testing patterns, and best practices. Key advantages of Vitest: - Native TypeScript support - Fast execution with HMR (Hot Module Replacement) - ESM and CommonJS support - Built-in mocking capabilities - Vite integration - Watch mode with smart re-running - Snapshot testing - Coverage reporting ## Installation and Setup ### Basic Installation ```bash # Install Vitest and related packages npm install -D vitest # For React testing npm install -D @testing-library/react @testing-library/jest-dom @testing-library/user-event # For TypeScript npm install -D @types/node ``` ### Vite Configuration ```typescript // vite.config.ts /// <reference types="vitest" /> import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], test: { globals: true, environment: 'jsdom', setupFiles: './src/test/setup.ts', css: true, reporters: ['verbose'], coverage: { reporter: ['text', 'json', 'html'], include: ['src/**/*'], exclude: [ 'node_modules/', 'src/test/', '**/*.d.ts', '**/*.test.{ts,tsx}', '**/*.spec.{ts,tsx}' ] } } }) ``` ### Test Setup File ```typescript // src/test/setup.ts import { expect, afterEach } from 'vitest' import { cleanup } from '@testing-library/react' import * as matchers from '@testing-library/jest-dom/matchers' // Extend Vitest's expect method with methods from react-testing-library expect.extend(matchers) // Cleanup after each test case afterEach(() => { cleanup() }) // Mock window.matchMedia Object.defineProperty(window, 'matchMedia', { writable: true, value: vi.fn().mockImplementation(query => ({ matches: false, media: query, onchange: null, addListener: vi.fn(), removeListener: vi.fn(), addEventListener: vi.fn(), removeEventListener: vi.fn(), dispatchEvent: vi.fn(), })), }) ``` ### TypeScript Configuration ```json // tsconfig.json { "compilerOptions": { "types": ["vitest/globals", "@testing-library/jest-dom"] } } ``` ### Package.json Scripts ```json { "scripts": { "test": "vitest", "test:ui": "vitest --ui", "test:run": "vitest run", "test:coverage": "vitest run --coverage", "test:watch": "vitest --watch" } } ``` ## Testing Patterns ### Basic Component Testing ```typescript // Button.test.tsx import { render, screen, fireEvent } from '@testing-library/react' import { expect, test, vi } from 'vitest' import { Button } from './Button' test('renders button with text', () => { render(<Button>Click me</Button>) expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument() }) test('calls onClick handler when clicked', () => { const handleClick = vi.fn() render(<Button onClick={handleClick}>Click me</Button>) fireEvent.click(screen.getByRole('button')) expect(handleClick).toHaveBeenCalledTimes(1) }) test('applies custom className', () => { render(<Button className="custom-class">Click me</Button>) expect(screen.getByRole('button')).toHaveClass('custom-class') }) ``` ### Testing with User Events ```typescript // UserForm.test.tsx import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { expect, test, vi } from 'vitest' import { UserForm } from './UserForm' test('submits form with user input', async () => { const user = userEvent.setup() const mockSubmit = vi.fn() render(<UserForm onSubmit={mockSubmit} />) await user.type(screen.getByLabelText(/name/i), 'John Doe') await user.type(screen.getByLabelText(/email/i), '[email protected]') await user.click(screen.getByRole('button', { name: /submit/i })) expect(mockSubmit).toHaveBeenCalledWith({ name: 'John Doe', email: '[email protected]' }) }) ``` ### Testing Hooks ```typescript // useCounter.test.ts import { renderHook, act } from '@testing-library/react' import { expect, test } from 'vitest' import { useCounter } from './useCounter' test('should increment counter', () => { const { result } = renderHook(() => useCounter()) act(() => { result.current.increment() }) expect(result.current.count).toBe(1) }) test('should decrement counter', () => { const { result } = renderHook(() => useCounter(10)) act(() => { result.current.decrement() }) expect(result.current.count).toBe(9) }) ``` ### Testing with Context ```typescript // ThemeProvider.test.tsx import { render, screen } from '@testing-library/react' import { expect, test } from 'vitest' import { ThemeProvider, ThemeContext } from './ThemeProvider' import { useContext } from 'react' const TestComponent = () => { const theme = useContext(ThemeContext) return <div data-testid="theme">{theme}</div> } test('provides theme context to children', () => { render( <ThemeProvider theme="dark"> <Test