Unit testing strategies for React components and UI elements to ensure reliability and maintainability.
Test individual components in isolation with mocked dependencies.
Test component interactions and data flow between connected components.
Catch visual regressions and ensure consistent UI appearance.
Testing button rendering, props, and click handlers
import { render, screen, fireEvent } from '@testing-library/react'
import { Button } from './Button'
describe('Button', () => {
it('renders with correct text', () => {
render(<Button>Click me</Button>)
expect(screen.getByText('Click me')).toBeInTheDocument()
})
it('calls onClick when clicked', () => {
const handleClick = vi.fn()
render(<Button onClick={handleClick}>Click me</Button>)
fireEvent.click(screen.getByText('Click me'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
it('applies variant styles correctly', () => {
render(<Button variant="primary">Primary</Button>)
expect(screen.getByText('Primary')).toHaveClass('bg-purple-500')
})
})Testing form input validation and error states
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { ContactForm } from './ContactForm'
describe('ContactForm', () => {
it('shows error for invalid email', async () => {
render(<ContactForm />)
const emailInput = screen.getByLabelText(/email/i)
fireEvent.change(emailInput, { target: { value: 'invalid-email' } })
fireEvent.blur(emailInput)
await waitFor(() => {
expect(screen.getByText(/valid email/i)).toBeInTheDocument()
})
})
it('submits form with valid data', async () => {
const onSubmit = vi.fn()
render(<ContactForm onSubmit={onSubmit} />)
fireEvent.change(screen.getByLabelText(/name/i), {
target: { value: 'John Doe' }
})
fireEvent.change(screen.getByLabelText(/email/i), {
target: { value: 'john@example.com' }
})
fireEvent.click(screen.getByText(/submit/i))
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith({
name: 'John Doe',
email: 'john@example.com'
})
})
})
})Fast unit test runner
User-centric testing utilities
E2E & visual testing
API mocking for tests
// vitest.config.ts
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
threshold: {
global: {
statements: 95,
branches: 90,
functions: 95,
lines: 95
}
}
}
}
})// src/test/setup.ts
import '@testing-library/jest-dom'
import { server } from './mocks/server'
// Mock IntersectionObserver
global.IntersectionObserver = vi.fn(() => ({
observe: vi.fn(),
disconnect: vi.fn(),
unobserve: vi.fn()
}))
// Setup MSW
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())