Философия Testing Library сформулирована в одном принципе: «Тестируйте так, как это делает пользователь». Пользователь не ищет в приложении `div` с `data-testid="submit-button"`. Он ищет кнопку по тексту на ней («Отправить»), по её роли (button) или по label связанной формы. Библиотека поощряет вас использовать именно такие, семантически осмысленные запросы, что делает тесты более устойчивыми к рефакторингу кода (когда меняются классы или структура DOM, но не функциональность) и, что ключевое, заставляет задуматься о доступности (accessibility).
Ядро экосистемы — это `DOM Testing Library`. Она предоставляет низкоуровневые API для работы с DOM. Однако чаще используются её адаптации для популярных фреймворков: `React Testing Library` для React, `Vue Testing Library` для Vue, `Angular Testing Library` для Angular, `Cypress Testing Library` для Cypress и даже `Selenium Testing Library` для Selenium. Это означает, что вы можете применять единый подход к запросам в разных технологических стеках.
Давайте разберем типы запросов (Queries), которые предлагает библиотека. Они делятся на три категории, каждая из которых соответствует тому, *как* пользователь находит элемент на странице.
- **GetBy...** (`getByText`, `getByRole`, `getByLabelText`): Запросы, которые ищут элемент, ожидая его немедленного присутствия в DOM. Если элемент не найден, тест падает сразу. Используются для элементов, которые должны быть на странице по умолчанию.
- **QueryBy...** (`queryByText`): Запросы, которые возвращают `null`, если элемент не найден. Это полезно для проверки *отсутствия* элемента («утверждаю, что сообщение об ошибке не отображается»).
- **FindBy...** (`findByText`): Асинхронные запросы, которые ждут появления элемента в течение заданного таймаута. Идеально для элементов, которые появляются после AJAX-запросов, таймеров или анимаций.
Практический пример. Представьте форму логина. Вместо хрупкого селектора `cy.get('#login-form > div:nth-child(2) > input')` в Testing Library вы напишете:
```
const emailInput = screen.getByLabelText(/email address/i);
const passwordInput = screen.getByLabelText(/password/i);
const submitButton = screen.getByRole('button', { name: /sign in/i });
```
Этот код не сломается, если разработчик переставит `div`-ы или изменит CSS-класс. Он сломается только если пропадет текст лейбла или роль кнопки, то есть если сломается сама логика взаимодействия для пользователя.
Помимо запросов, библиотека предоставляет утилиты для симуляции пользовательских событий (`fireEvent` или более продвинутый `userEvent` из отдельного пакета). `userEvent` симулирует действия более реалистично: `click()` вызывает не только событие клика, но и фокус, а `type()` имитирует нажатие клавиш по одной. Это приближает тесты к реальному поведению.
Как интегрировать это в процесс? Для модульного и интеграционного тестирования компонентов React/Vue/Angular вы будете использовать соответствующую адаптацию вместе с Jest или Vitest. Для сквозного (E2E) тестирования в Cypress установите плагин `@testing-library/cypress`, и вы сможете использовать те же запросы (`cy.findByRole(...)`) прямо в своих Cypress-тестах, объединяя мощь E2E-фреймворка с философией пользовательских запросов.
Критика и подводные камни тоже есть. Иногда найти уникальный и осмысленный запрос для сложного или плохо структурированного компонента может быть непросто. Это, однако, часто указывает на проблемы с доступностью или семантикой самого компонента, что делает тест еще и инструментом рефакторинга. Также новичкам требуется время, чтобы привыкнуть к работе с ролями и деревом доступности.
В итоге, освоение Testing Library — это инвестиция в качество и надежность ваших тестов. Вы начинаете писать тесты, которые проверяют не реализацию, а поведение. Вы неявно начинаете улучшать доступность вашего приложения, потому что тесты, написанные через `getByRole`, просто не пройдут, если у элемента нет правильной семантики. Для тестировщика это переход от роли «проверяющего селекторы» к роли «гаранта пользовательского опыта», что делает работу более ценной и соответствующей современным стандартам фронтенд-разработки.
Комментарии (12)