Переиспользование кода между тестами
В процессе разработки и тестирования приложений с использованием React Testing Library возникает необходимость в переиспользовании кода между различными тестами. Это помогает избежать дублирования, упрощает поддержку тестов и делает их более читаемыми. В этой части рассматриваются ключевые подходы к переиспользованию тестов и создание общих утилит.
Для эффективного использования кода между тестами важно выделить общие части тестов и организовать их так, чтобы они могли быть легко переиспользованы. Это возможно благодаря использованию функций и вспомогательных утилит, которые инкапсулируют повторяющийся код.
Одним из самых простых способов переиспользования кода является создание вспомогательных функций. Это особенно актуально, если набор действий, таких как рендеринг компонента, взаимодействие с элементами или асинхронные действия, повторяется в нескольких тестах.
Пример:
import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';
function renderMyComponent() {
return render(<MyComponent />);
}
test('отображение текста в компоненте', () => {
renderMyComponent();
expect(screen.getByText('Привет, мир!')).toBeInTheDocument();
});
test('обработка клика по кнопке', () => {
renderMyComponent();
fireEvent.click(screen.getByRole('button', { name: /кликни меня/i }));
expect(screen.getByText('Кнопка нажата!')).toBeInTheDocument();
});
В этом примере функция renderMyComponent инкапсулирует
процесс рендеринга компонента, который затем используется в нескольких
тестах. Это упрощает добавление новых тестов и уменьшает количество
дублирования.
Для тестов, где необходимо выполнить определённые действия до каждого
теста (например, настроить состояние компонента или выполнить
рендеринг), можно использовать хуки beforeEach или
beforeAll. Эти хуки позволяют подготовить тестовое
окружение перед выполнением каждого теста или перед всеми тестами в
рамках одного набора.
Пример:
import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';
let container;
beforeEach(() => {
container = render(<MyComponent />);
});
test('отображение текста в компоненте', () => {
expect(screen.getByText('Привет, мир!')).toBeInTheDocument();
});
test('обработка клика по кнопке', () => {
fireEvent.click(screen.getByRole('button', { name: /кликни меня/i }));
expect(screen.getByText('Кнопка нажата!')).toBeInTheDocument();
});
Здесь beforeEach используется для рендеринга компонента
перед каждым тестом, что исключает необходимость повторного написания
однотипного кода в каждом тесте. Это также полезно, если компонент имеет
сложные состояния, которые должны быть подготовлены до начала теста.
Часто в процессе тестирования требуется использовать мокированные данные или функции для изоляции тестируемого компонента от внешних зависимостей. Это особенно актуально при работе с API или глобальными состояниями.
Для мокирования данных можно использовать такие утилиты как
jest.mock. Это позволяет заменить реальные функции на
тестовые заглушки.
Пример:
import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';
import { fetchData } from './api';
jest.mock('./api');
beforeEach(() => {
fetchData.mockResolvedValue({ data: 'Заглушка данных' });
});
test('отображение данных после запроса', async () => {
render(<MyComponent />);
expect(await screen.findByText('Заглушка данных')).toBeInTheDocument();
});
В этом примере используется мок для функции fetchData,
что позволяет протестировать компонент без реального запроса к серверу.
Это позволяет избежать зависимости от внешних систем и ускоряет
выполнение тестов.
В некоторых случаях может быть полезно создать кастомную рендер-функцию, которая будет не только рендерить компонент, но и обеспечивать настройку состояния, передаваемых данных или моки. Это может быть полезно для более сложных компонентов, которые требуют специфической настройки.
Пример:
import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';
function renderWithProps(props) {
return render(<MyComponent {...props} />);
}
test('проверка поведения компонента с кастомными пропсами', () => {
renderWithProps({ text: 'Новый текст' });
expect(screen.getByText('Новый текст')).toBeInTheDocument();
});
Здесь renderWithProps позволяет тестировать компонент с
разными наборами пропсов, что делает тесты более универсальными и
гибкими.
Когда компоненты имеют сложную логику или состояние, создание пользовательских хуков может значительно улучшить читаемость и переиспользуемость кода. Тесты для таких компонентов могут также выигрывать от использования кастомных хуков для подготовки тестовых данных.
Пример:
import { render, screen } from '@testing-library/react';
import { useUserData } from './useUserData';
import MyComponent from './MyComponent';
jest.mock('./useUserData');
test('отображение данных пользователя', () => {
useUserData.mockReturnValue({ name: 'Иван' });
render(<MyComponent />);
expect(screen.getByText('Привет, Иван')).toBeInTheDocument();
});
Здесь используется мок для пользовательского хука, что позволяет изолировать тестируемый компонент от внутренней логики получения данных.
В сложных приложениях часто используются большие объёмы данных, которые могут повторяться в разных тестах. Вместо того чтобы создавать эти данные каждый раз заново, можно вынести их в отдельные переменные или файлы и переиспользовать в различных тестах.
Пример:
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
import { mockUserData } from './mockData';
test('проверка отображения данных', () => {
render(<MyComponent user={mockUserData} />);
expect(screen.getByText(mockUserData.name)).toBeInTheDocument();
});
Здесь mockUserData является общими данными, которые
могут использоваться в нескольких тестах.
Переиспользование кода между тестами в React Testing Library способствует улучшению читаемости, уменьшению дублирования и ускорению процесса тестирования. Использование вспомогательных функций, хуков, мокирования и кастомных рендер-функций позволяет более гибко подходить к тестированию компонентов, снижая количество повторений кода и повышая поддерживаемость тестов в долгосрочной перспективе.