Тестирование компонентов в React с помощью React Testing Library (RTL) подразумевает не только написание тестов, но и поддержание их в удобном и читаемом виде. Для упрощения процесса тестирования и уменьшения дублирования кода в проектах часто используют различные утилиты, которые помогают создавать стабильные, масштабируемые и поддерживаемые тесты.
В этой главе рассматриваются утилиты, которые облегчают работу с тестами, помогая избежать избыточного кода и улучшить читабельность тестовых файлов.
Иногда в тестах приходится многократно взаимодействовать с одинаковыми частями компонента — например, элементы формы, кнопки или выпадающие списки. В таких случаях целесообразно создать утилиты, которые инкапсулируют повторяющиеся действия.
// utils/test-utils.js
export function clickButton(screen, buttonText) {
const button = screen.getByText(buttonText);
userEvent.click(button);
}
Эта утилита позволяет легко кликать по кнопке, передавая ей текст. Использование такой функции помогает избежать повторений в тестах.
import { render, screen } from '@testing-library/react';
import { clickButton } from './utils/test-utils';
import MyComponent from './MyComponent';
test('should perform an action when button is clicked', () => {
render(<MyComponent />);
clickButton(screen, 'Submit');
expect(screen.getByText('Form submitted')).toBeInTheDocument();
});
Пример показывает, как можно использовать утилиту в тестах, чтобы сделать их более компактными и удобными для чтения.
Тесты, которые взаимодействуют с API или выполняют асинхронные операции, требуют мока данных, чтобы избежать настоящих сетевых запросов. Для этого можно использовать утилиты для настройки мока API.
msw// utils/mock-api.js
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
rest.get('/api/user', (req, res, ctx) => {
return res(ctx.json({ name: 'John Doe', age: 30 }));
})
);
export function startServer() {
server.listen();
}
export function stopServer() {
server.close();
}
В этом примере используется библиотека MSW (Mock Service Worker) для
мока API. Функции startServer и stopServer
могут быть вызваны в тестах для имитации серверных запросов. Это
позволяет тестировать поведение компонентов без зависимости от реальных
серверных запросов.
import { render, screen, waitFor } from '@testing-library/react';
import { startServer, stopServer } from './utils/mock-api';
import UserProfile from './UserProfile';
beforeAll(() => startServer());
afterAll(() => stopServer());
test('renders user profile after API call', async () => {
render(<UserProfile />);
await waitFor(() => screen.getByText('John Doe'));
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('30')).toBeInTheDocument();
});
Здесь UserProfile — это компонент, который запрашивает
данные с API. Благодаря моку, тест не зависит от реального сервера, что
делает его быстрым и независимым.
Для работы с формами часто требуются дополнительные утилиты, которые помогают эмулировать ввод пользователя, проверку значений и их валидацию.
// utils/form-utils.js
import { userEvent } from '@testing-library/user-event';
export function fillForm(screen, data) {
for (const [label, value] of Object.entries(data)) {
const input = screen.getByLabelText(label);
userEvent.type(input, value);
}
}
Эта утилита позволяет быстро заполнять форму с данными, переданными в объекте. Например, для формы с полями «Имя» и «Email» можно передать объект с данными:
import { render, screen } from '@testing-library/react';
import { fillForm } from './utils/form-utils';
import MyForm from './MyForm';
test('submits form with correct data', async () => {
render(<MyForm />);
fillForm(screen, { 'Name': 'Alice', 'Email': 'alice@example.com' });
const submitButton = screen.getByText('Submit');
userEvent.click(submitButton);
expect(screen.getByText('Form submitted')).toBeInTheDocument();
});
Утилита значительно сокращает код теста и улучшает его читаемость.
В некоторых случаях полезно создавать утилиты для работы с состоянием компонента, например, с состоянием модальных окон, сворачиваемых панелей и т. д. Утилита для управления состоянием может помочь при тестировании различных вариантов состояния компонента.
// utils/modal-utils.js
import { userEvent } from '@testing-library/user-event';
export function openModal(screen) {
const openButton = screen.getByText('Open Modal');
userEvent.click(openButton);
}
export function closeModal(screen) {
const closeButton = screen.getByText('Close Modal');
userEvent.click(closeButton);
}
Эти утилиты позволяют эффективно тестировать открытие и закрытие модальных окон без дублирования кода.
import { render, screen } from '@testing-library/react';
import { openModal, closeModal } from './utils/modal-utils';
import ModalComponent from './ModalComponent';
test('should open and close modal', () => {
render(<ModalComponent />);
openModal(screen);
expect(screen.getByText('Modal Content')).toBeInTheDocument();
closeModal(screen);
expect(screen.queryByText('Modal Content')).not.toBeInTheDocument();
});
Переиспользуемость: Утилиты должны быть независимыми и многократно используемыми в разных тестах. Они должны решать одну конкретную задачу, чтобы их можно было легко интегрировать в различные контексты.
Читабельность: Важно, чтобы утилиты были простыми и понятными. Сложные, многозадачные утилиты только усложняют тесты. Каждый метод должен иметь очевидную задачу.
Тестируемость: Утилиты должны быть хорошо тестируемыми. Для этого необходимо избегать логики, которая зависит от внешних условий (например, времени или состояния приложения).
Инкапсуляция: Стоит минимизировать количество кода, который появляется в тестах, с помощью инкапсуляции общей логики в утилитах. Таким образом, тесты остаются компактными, а вся сложность сосредоточена в отдельных функциях.
Создание утилит для тестов — это один из способов повысить стабильность тестирования, улучшить читаемость и упростить поддержку тестов.