Testing Library: подход
Testing Library — семейство библиотек для тестирования UI-компонентов через DOM-взаимодействие, основанных на принципе «тестируй поведение, а не реализацию».
Зачем нужно
Тест, проверяющий component.state.isLoading === false, сломается при любом рефакторинге состояния. Тест, проверяющий что «спиннер исчез и данные видны» — не сломается. Testing Library направляет к написанию тестов, устойчивых к изменениям реализации.
Где используется
- React Testing Library — React-компоненты
- Vue Testing Library — Vue компоненты
- Angular Testing Library — Angular
- Svelte Testing Library, Solid Testing Library
- DOM Testing Library — ванильный DOM
Основной контент
Ключевой принцип
Guiding principle Testing Library: «Чем больше тесты напоминают то, как реальные пользователи используют ваше ПО, тем больше уверенности они дают».
Приоритет запросов (queries priority)
Testing Library определяет иерархию запросов от наиболее к наименее предпочтительным:
1. Accessible queries (видят screen readers и пользователи)
- getByRole
- getByLabelText
- getByPlaceholderText
- getByText
- getByDisplayValue
2. Semantic queries
- getByAltText
- getByTitle
3. Test IDs (последний выбор)
- getByTestId
Варианты запросов: get, query, find
// getBy* — синхронный, выбрасывает если не найдено
screen.getByRole('button');
// queryBy* — синхронный, возвращает null если не найдено (для проверки отсутствия)
expect(screen.queryByRole('alert')).not.toBeInTheDocument;
// findBy* — асинхронный (waitFor внутри), ждёт появления элемента
await screen.findByText('Данные загружены');
AllBy варианты
// Если элементов несколько
const buttons = screen.getAllByRole('button');
expect(buttons).toHaveLength(3);
// Проверка что ни одного нет
expect(screen.queryAllByRole('alert')).toHaveLength(0);
userEvent vs fireEvent
import userEvent from '@testing-library/user-event';
// userEvent — реалистичная симуляция (рекомендуется)
const user = userEvent.setup;
await user.click(button);
await user.type(input, 'Hello world');
await user.keyboard('{Enter}');
await user.selectOptions(select, 'option-value');
// fireEvent — низкоуровневый dispatch события (используй только если userEvent не подходит)
import { fireEvent } from '@testing-library/react';
fireEvent.change(input, { target: { value: 'test' } });
waitFor для асинхронных обновлений
import { waitFor } from '@testing-library/react';
await waitFor(() => {
expect(screen.getByText('Загрузка завершена')).toBeInTheDocument;
});
// Или через findBy*
await screen.findByText('Загрузка завершена');
Что НЕ нужно тестировать
- Внутренние детали: state, props, refs
- Методы компонента напрямую
- Структура DOM (точные CSS-классы, теги)
Частые ошибки
getByTextдля динамического текста — тест упадёт если изменится перевод; используйgetByRoleсname- Тест реализации через
.instance— Testing Library не предоставляет доступ к внутренностям намеренно actwarnings — появляются при обновлении state без обёртки; решается черезawait userEvent.*илиawait waitFor(...)
Связанные темы
- _MOC Тестирование
- React Testing Library -- основы
- Тестирование рендеринга
- Тестирование пользовательских событий
- Тестирование форм