Перейти к содержимому

12. React Testing Library

RTL

“Test the way your users use it, not implementation details”

React Testing Library (RTL) тестирует поведение компонента с точки зрения пользователя, а не внутреннюю реализацию.

Окно терминала
npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event
src/setupTests.js
import '@testing-library/jest-dom';
// jest.config.js
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterFramework: ['<rootDir>/src/setupTests.js'],
};
// vitest.config.ts
export default defineConfig({
test: {
environment: 'jsdom',
setupFiles: ['./src/setupTests.ts'],
},
})
Button.tsx
export function Button({ onClick, children, disabled = false }) {
return (
<button onClick={onClick} disabled={disabled}>
{children}
</button>
);
}
// Button.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './Button';
test('renders button with text', () => {
render(<Button>Click me</Button>);
const button = screen.getByRole('button', { name: /click me/i });
expect(button).toBeInTheDocument();
});
test('calls onClick when clicked', async () => {
const user = userEvent.setup();
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Submit</Button>);
await user.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
test('button is disabled', () => {
render(<Button disabled>Submit</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
// DOM-специфичные матчеры (из @testing-library/jest-dom)
expect(element).toBeInTheDocument(); // элемент в DOM
expect(element).toBeVisible(); // элемент виден
expect(element).toBeDisabled(); // disabled
expect(element).toBeEnabled(); // не disabled
expect(element).toBeChecked(); // checkbox/radio checked
expect(element).toHaveValue('text'); // значение input
expect(element).toHaveTextContent('hello'); // текст элемента
expect(element).toHaveClass('btn-primary'); // CSS класс
expect(element).toHaveAttribute('href', '/home'); // атрибут
expect(element).toHaveFocus(); // в фокусе
expect(element).toBeRequired(); // required атрибут
expect(element).toBeValid(); // форма валидна
expect(element).toBeInvalid(); // форма невалидна
UserCard.tsx
interface User {
name: string;
email: string;
role: 'admin' | 'user';
avatar?: string;
}
export function UserCard({ user }: { user: User }) {
return (
<div data-testid="user-card">
{user.avatar ? (
<img src={user.avatar} alt={`${user.name} avatar`} />
) : (
<div data-testid="avatar-placeholder">{user.name[0]}</div>
)}
<h2>{user.name}</h2>
<p>{user.email}</p>
{user.role === 'admin' && <span data-testid="admin-badge">Admin</span>}
</div>
);
}
// UserCard.test.tsx
describe('UserCard', () => {
const defaultUser = {
name: 'Alice',
role: 'user' as const,
};
test('renders user info', () => {
render(<UserCard user={defaultUser} />);
expect(screen.getByText('Alice')).toBeInTheDocument();
expect(screen.getByText('[email protected]')).toBeInTheDocument();
});
test('shows admin badge for admins', () => {
render(<UserCard user={{ ...defaultUser, role: 'admin' }} />);
expect(screen.getByTestId('admin-badge')).toBeInTheDocument();
});
test('hides admin badge for regular users', () => {
render(<UserCard user={defaultUser} />);
expect(screen.queryByTestId('admin-badge')).not.toBeInTheDocument();
});
test('shows avatar image when provided', () => {
render(<UserCard user={{ ...defaultUser, avatar: '/img/alice.jpg' }} />);
const img = screen.getByAltText('Alice avatar');
expect(img).toHaveAttribute('src', '/img/alice.jpg');
});
test('shows placeholder when no avatar', () => {
render(<UserCard user={defaultUser} />);
expect(screen.getByTestId('avatar-placeholder')).toHaveTextContent('A');
});
});
  1. Установи RTL и напиши тесты для компонента Alert (info/warning/error)
  2. Протестируй компонент Badge с разными вариантами отображения
  3. Протестируй список UserList — отрисовку нескольких элементов и пустого состояния
  • RTL тестирует поведение, не реализацию
  • getByRole — приоритетный способ поиска элементов
  • jest-dom добавляет удобные DOM-матчеры
  • data-testid — только когда нет семантического способа найти элемент