Testing frameworks and tools for ensuring code quality in React applications.
Description: Delightful JavaScript testing framework with a focus on simplicity and developer experience.
Key Features:
- Zero configuration
- Snapshot testing
- Code coverage
- Mocking capabilities
- Parallel test execution
- Built-in assertions
- Watch mode
Installation:
npm install --save-dev jest
# or with React
npm install --save-dev jest @testing-library/jest-domConfiguration (jest.config.js):
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
moduleNameMapping: {
'^@/(.*)$': '<rootDir>/src/$1'
},
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts'
]
};Basic Usage:
// utils.test.js
import { add, multiply, formatCurrency } from './utils';
describe('Math utilities', () => {
test('adds two numbers correctly', () => {
expect(add(2, 3)).toBe(5);
expect(add(-1, 1)).toBe(0);
});
test('multiplies two numbers correctly', () => {
expect(multiply(2, 3)).toBe(6);
expect(multiply(-2, 3)).toBe(-6);
});
});
describe('Format utilities', () => {
test('formats currency correctly', () => {
expect(formatCurrency(123.45)).toBe('$123.45');
expect(formatCurrency(0)).toBe('$0.00');
});
});
// Async testing
describe('API utilities', () => {
test('fetches user data', async () => {
const user = await fetchUser(1);
expect(user).toHaveProperty('id', 1);
expect(user).toHaveProperty('name');
});
test('handles fetch errors', async () => {
await expect(fetchUser(999)).rejects.toThrow('User not found');
});
});
// Mocking
jest.mock('./api', () => ({
fetchUser: jest.fn()
}));
import { fetchUser } from './api';
describe('With mocks', () => {
test('uses mocked fetchUser', async () => {
fetchUser.mockResolvedValue({ id: 1, name: 'John' });
const user = await fetchUser(1);
expect(fetchUser).toHaveBeenCalledWith(1);
expect(user.name).toBe('John');
});
});Use Cases: Unit testing, integration testing, utility function testing
Description: Simple and complete testing utilities for React components that encourage good testing practices.
Key Features:
- User-centric testing
- Accessibility testing
- Component testing utilities
- Custom render functions
- Query utilities
- Async utilities
- Jest integration
Installation:
npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-eventSetup (setupTests.js):
import '@testing-library/jest-dom';Basic Usage:
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import App from './App';
describe('App Component', () => {
test('renders welcome message', () => {
render(<App />);
expect(screen.getByText(/welcome/i)).toBeInTheDocument();
});
test('handles button click', async () => {
const user = userEvent.setup();
render(<App />);
const button = screen.getByRole('button', { name: /click me/i });
await user.click(button);
expect(screen.getByText(/button clicked/i)).toBeInTheDocument();
});
test('handles form submission', async () => {
const user = userEvent.setup();
render(<App />);
const input = screen.getByLabelText(/name/i);
const submitButton = screen.getByRole('button', { name: /submit/i });
await user.type(input, 'John Doe');
await user.click(submitButton);
await waitFor(() => {
expect(screen.getByText(/hello john doe/i)).toBeInTheDocument();
});
});
});
// Testing custom hooks
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';
describe('useCounter hook', () => {
test('initializes with default value', () => {
const { result } = renderHook(() => useCounter());
expect(result.current.count).toBe(0);
});
test('increments counter', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
});
// Testing with context
import { render } from '@testing-library/react';
import { ThemeProvider } from './ThemeContext';
const renderWithTheme = (ui, { theme = 'light' } = {}) => {
return render(
<ThemeProvider value={theme}>
{ui}
</ThemeProvider>
);
};
test('renders with dark theme', () => {
renderWithTheme(<App />, { theme: 'dark' });
expect(document.body).toHaveClass('dark-theme');
});Use Cases: Component testing, user interaction testing, accessibility testing
Description: Fast, easy and reliable testing for anything that runs in a browser with end-to-end testing capabilities.
Key Features:
- End-to-end testing
- Time travel debugging
- Real browser testing
- Network stubbing
- Screenshot and video recording
- Cross-browser testing
- Component testing
Installation:
npm install --save-dev cypress
npx cypress openConfiguration (cypress.config.js):
module.exports = {
e2e: {
baseUrl: 'http://localhost:3000',
viewportWidth: 1280,
viewportHeight: 720,
video: true,
screenshotOnRunFailure: true,
},
component: {
devServer: {
framework: 'create-react-app',
bundler: 'webpack',
},
},
};Basic Usage:
// e2e tests - cypress/e2e/user-flow.cy.js
describe('User Authentication Flow', () => {
beforeEach(() => {
cy.visit('/');
});
it('should display login form', () => {
cy.get('[data-testid="login-form"]').should('be.visible');
cy.get('input[name="email"]').should('exist');
cy.get('input[name="password"]').should('exist');
});
it('should login successfully with valid credentials', () => {
cy.get('input[name="email"]').type('user@example.com');
cy.get('input[name="password"]').type('password123');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
cy.get('[data-testid="user-menu"]').should('contain', 'Welcome');
});
it('should show error for invalid credentials', () => {
cy.get('input[name="email"]').type('invalid@example.com');
cy.get('input[name="password"]').type('wrongpassword');
cy.get('button[type="submit"]').click();
cy.get('[data-testid="error-message"]').should('be.visible');
cy.get('[data-testid="error-message"]').should('contain', 'Invalid credentials');
});
});
// API testing
describe('API Integration', () => {
it('should fetch user data', () => {
cy.intercept('GET', '/api/users/*', { fixture: 'user.json' }).as('getUser');
cy.visit('/profile');
cy.wait('@getUser');
cy.get('[data-testid="user-name"]').should('contain', 'John Doe');
cy.get('[data-testid="user-email"]').should('contain', 'john@example.com');
});
});
// Component testing - cypress/component/Button.cy.jsx
import Button from '../../src/components/Button';
describe('Button Component', () => {
it('renders with correct text', () => {
cy.mount(<Button>Click me</Button>);
cy.contains('Click me').should('be.visible');
});
it('handles click events', () => {
const onClick = cy.stub();
cy.mount(<Button onClick={onClick}>Click me</Button>);
cy.get('button').click();
cy.then(() => {
expect(onClick).to.have.been.called;
});
});
it('applies correct styling variants', () => {
cy.mount(<Button variant="primary">Primary Button</Button>);
cy.get('button').should('have.class', 'btn-primary');
});
});Use Cases: End-to-end testing, user journey testing, API integration testing
describe('ComponentName', () => {
test('should do something when condition is met', () => {
// Arrange
const props = { title: 'Test Title' };
// Act
render(<Component {...props} />);
// Assert
expect(screen.getByText('Test Title')).toBeInTheDocument();
});
});test('should update input value when user types', async () => {
const user = userEvent.setup();
render(<InputComponent />);
const input = screen.getByRole('textbox');
await user.type(input, 'Hello World');
expect(input).toHaveValue('Hello World');
});test('should display data after loading', async () => {
render(<DataComponent />);
expect(screen.getByText('Loading...')).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByText('Data loaded')).toBeInTheDocument();
});
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
});// Mock API calls
jest.mock('./api', () => ({
fetchData: jest.fn().mockResolvedValue({ id: 1, name: 'Test' })
}));
// Mock React Router
import { MemoryRouter } from 'react-router-dom';
const renderWithRouter = (ui, { route = '/' } = {}) => {
return render(
<MemoryRouter initialEntries={[route]}>
{ui}
</MemoryRouter>
);
};| Tool | Testing Level | Setup Complexity | Browser Support | Use Case |
|---|---|---|---|---|
| Jest | Unit/Integration | Low | Node.js | Logic testing |
| React Testing Library | Component | Medium | jsdom | Component testing |
| Cypress | E2E/Component | Medium | Real browsers | User flow testing |
- Jest: For unit testing utility functions, hooks, and business logic
- React Testing Library: For testing React components and user interactions
- Cypress: For end-to-end testing and testing complete user journeys
import { render } from '@testing-library/react';
import { ThemeProvider } from './ThemeProvider';
import { Router } from 'react-router-dom';
const AllTheProviders = ({ children }) => {
return (
<Router>
<ThemeProvider>
{children}
</ThemeProvider>
</Router>
);
};
const customRender = (ui, options) =>
render(ui, { wrapper: AllTheProviders, ...options });
export * from '@testing-library/react';
export { customRender as render };// test-utils.js
export const createMockUser = (overrides = {}) => ({
id: 1,
name: 'John Doe',
email: 'john@example.com',
...overrides
});
export const renderWithUser = (ui, { user = createMockUser() } = {}) => {
return render(
<UserProvider value={user}>
{ui}
</UserProvider>
);
};