Одной из важнейших задач при тестировании компонентов с использованием React Testing Library (RTL) является грамотное покрытие как положительных, так и отрицательных сценариев. Понимание того, как правильно создавать и запускать такие тесты, позволяет удостовериться в корректной работе приложения в различных условиях.
Положительные тесты (или тесты “happy path”) предназначены для проверки того, что приложение или компонент работает правильно, когда все условия соблюдены и пользователь взаимодействует с системой в рамках предполагаемого сценария.
При создании положительных тестов важно сосредоточиться на функциональности, которая должна успешно работать. Например, если у нас есть форма с полями ввода и кнопкой отправки, положительный тест может проверить, что при правильном вводе данных форма отправляется корректно и показывается успешное сообщение.
import { render, screen, fireEvent } from '@testing-library/react';
import MyForm from './MyForm';
test('форма отправляется при правильном вводе данных', () => {
render(<MyForm />);
// Поиск элементов
const inputName = screen.getByLabelText(/имя/i);
const inputEmail = screen.getByLabelText(/email/i);
const submitButton = screen.getByRole('button', { name: /отправить/i });
// Ввод данных
fireEvent.change(inputName, { target: { value: 'Иван' } });
fireEvent.change(inputEmail, { target: { value: 'ivan@example.com' } });
// Отправка формы
fireEvent.click(submitButton);
// Проверка результата
const successMessage = screen.getByText(/форма успешно отправлена/i);
expect(successMessage).toBeInTheDocument();
});
В этом примере тест проверяет, что при вводе корректных данных и отправке формы, появляется сообщение об успешной отправке. Это классический положительный тест, который следует ожидать в случае правильной работы компонента.
fireEvent.change или user-event.type.Отрицательные тесты проверяют компоненты на ошибки или исключения в ситуациях, когда что-то идет не так. Это могут быть неверные данные, неправильное поведение пользователя или неполные данные. Такие тесты необходимы для того, чтобы убедиться, что система корректно обрабатывает исключительные ситуации и не ведет себя неожиданно.
import { render, screen, fireEvent } from '@testing-library/react';
import MyForm from './MyForm';
test('показывает ошибку при неверном email', () => {
render(<MyForm />);
// Поиск элементов
const inputEmail = screen.getByLabelText(/email/i);
const submitButton = screen.getByRole('button', { name: /отправить/i });
// Ввод некорректных данных
fireEvent.change(inputEmail, { target: { value: 'invalid-email' } });
// Отправка формы
fireEvent.click(submitButton);
// Проверка ошибки
const errorMessage = screen.getByText(/некорректный email/i);
expect(errorMessage).toBeInTheDocument();
});
В этом тесте проверяется, что при вводе некорректного email пользователю показывается сообщение об ошибке. Такие тесты важны для обеспечения надежности компонента и предотвращения проблем в реальных условиях эксплуатации.
Для полного тестирования компонента, следует создавать тесты для обоих типов случаев. Однако важно, чтобы положительные и отрицательные тесты не перекрывались. Например, если компонент проверяет правильность email, то положительный тест должен проверить корректный формат, а отрицательный — некорректный.
При комбинировании этих тестов важно понимать, что тесты должны быть независимыми. То есть изменение состояния в одном тесте не должно влиять на результат другого. Это гарантирует стабильность тестового набора и позволяет быстро выявить источник ошибки.
React Testing Library предлагает удобный API для взаимодействия с
компонентами, который симулирует поведение реального пользователя. Для
тестирования негативных сценариев часто используют события, такие как
fireEvent.change, fireEvent.click, и методы из
библиотеки user-event, которая позволяет симулировать более
сложные действия пользователя (например, ввод с клавиатуры или
взаимодействие с элементами).
import userEvent from '@testing-library/user-event';
test('показывает ошибку при пустом поле', async () => {
render(<MyForm />);
const inputName = screen.getByLabelText(/имя/i);
const submitButton = screen.getByRole('button', { name: /отправить/i });
await userEvent.type(inputName, '');
userEvent.click(submitButton);
const errorMessage = screen.getByText(/это поле обязательно/i);
expect(errorMessage).toBeInTheDocument();
});
Использование user-event позволяет не только
сымитировать действия, но и учитывать такие нюансы, как асинхронные
события или сложные взаимодействия.
Важно помнить, что многие компоненты работают асинхронно, особенно
при взаимодействии с сервером или обработке сложных форм. В таких
случаях необходимо использовать асинхронные ожидания, такие как
findBy, waitFor, или
waitForElementToBeRemoved, чтобы дождаться появления или
исчезновения элементов на странице. Это помогает избежать ложных
срабатываний тестов, когда элементы ещё не обновились.
test('должен показать сообщение о загрузке', async () => {
render(<MyComponent />);
fireEvent.click(screen.getByRole('button', { name: /загрузить/i }));
// Ожидание появления индикатора загрузки
const loadingMessage = await screen.findByText(/загрузка.../i);
expect(loadingMessage).toBeInTheDocument();
});
Таким образом, правильное использование положительных и отрицательных тестов с учётом асинхронности и пользовательского поведения помогает создать более надёжную систему, устойчивая к ошибкам и корректно обрабатывающая все возможные ситуации.