В процессе написания тестов с использованием React Testing Library, важно учитывать, что многие тесты могут повторяться. Это касается часто используемых операций, таких как рендеринг компонентов, ожидание элементов или взаимодействие с ними. Вместо того чтобы дублировать код в каждом тесте, существует возможность выделить общий функционал в отдельные утилиты и переиспользовать его. Это помогает улучшить читаемость и поддерживаемость тестов, а также облегчить их расширение.
Переиспользуемые утилиты тестирования должны быть инкапсулированы и максимально абстрагированы от специфики конкретных тестов. Это обеспечит удобство использования этих утилит в различных сценариях и компонентах. Важно, чтобы такие утилиты могли быть легко подключены и использованы в разных тестах без необходимости переписывать код.
Одним из самых распространённых случаев, когда необходимо повторно использовать код, является рендеринг компонентов с определёнными пропсами. Вместо того чтобы каждый раз вручную передавать пропсы, можно создать утилиту, которая будет принимать параметры и рендерить компонент с нужными данными.
Пример:
// utils/renderWithProviders.js
import { render } from '@testing-library/react';
import { MyContextProvider } from '../context';
export function renderWithProviders(ui, { providerProps = {} } = {}) {
return render(
<MyContextProvider {...providerProps}>
{ui}
</MyContextProvider>
);
}
Здесь создаётся утилита renderWithProviders, которая
оборачивает компонент в контекстный провайдер. Такой подход полезен,
когда необходимо передать какие-то глобальные состояния или настройки,
которые требуются для рендеринга компонента.
Теперь, чтобы использовать эту утилиту в тестах, достаточно импортировать её и передать нужный компонент:
import { renderWithProviders } from './utils/renderWithProviders';
import MyComponent from './MyComponent';
test('MyComponent renders correctly', () => {
const { getByText } = renderWithProviders(<MyComponent />);
expect(getByText(/Hello/)).toBeInTheDocument();
});
Ещё одна важная часть тестирования — это взаимодействие с элементами в компоненте. Часто используются операции типа клика по кнопке или ввода текста в поле. Эти действия можно абстрагировать в отдельные утилиты, чтобы упростить тесты.
Пример утилиты для клика:
// utils/clickButton.js
import { fireEvent } from '@testing-library/react';
export function clickButton(button) {
fireEvent.click(button);
}
Теперь, в тестах можно использовать эту утилиту для кликов по кнопкам:
import { clickButton } from './utils/clickButton';
import { renderWithProviders } from './utils/renderWithProviders';
import MyComponent from './MyComponent';
test('MyComponent triggers the event on button click', () => {
const { getByText } = renderWithProviders(<MyComponent />);
const button = getByText(/Click me/);
clickButton(button);
expect(getByText(/Clicked/)).toBeInTheDocument();
});
Для работы с асинхронными компонентами часто используется
waitFor или findBy, чтобы подождать, пока
компонент обновится. Для улучшения читаемости тестов и исключения
повторений можно создать утилиту, которая будет обрабатывать
ожидания.
Пример:
// utils/waitForElement.js
import { waitFor } from '@testing-library/react';
export async function waitForElement(element, options = {}) {
return await waitFor(() => {
expect(element).toBeInTheDocument();
}, options);
}
Использование этой утилиты:
import { renderWithProviders } from './utils/renderWithProviders';
import { waitForElement } from './utils/waitForElement';
import MyComponent from './MyComponent';
test('MyComponent waits for async content', async () => {
const { getByText } = renderWithProviders(<MyComponent />);
const asyncElement = await getByText(/Loading.../);
await waitForElement(asyncElement);
expect(getByText(/Content loaded/)).toBeInTheDocument();
});
Для тестирования компонентов, которые зависят от внешних API, полезно создавать утилиты для мока данных. Это позволяет эмулировать ответы от серверов и тестировать компоненты без необходимости подключения к настоящему API.
Пример утилиты для мокирования данных:
// utils/mockApi.js
export function mockApi(response = { data: [] }) {
jest.spyOn(global, 'fetch').mockResolvedValueOnce({
json: jest.fn().mockResolvedValue(response),
});
}
Использование утилиты в тестах:
import { mockApi } from './utils/mockApi';
import { renderWithProviders } from './utils/renderWithProviders';
import MyComponent from './MyComponent';
test('MyComponent fetches data correctly', async () => {
mockApi({ data: [{ id: 1, name: 'Test' }] });
const { getByText } = renderWithProviders(<MyComponent />);
expect(getByText(/Loading.../)).toBeInTheDocument();
await waitForElement(getByText(/Test/));
});
Рекомендуется организовывать утилиты в отдельные файлы или папки, чтобы поддерживать хорошую структуру проекта. Например:
/utils
/renderWithProviders.js
/clickButton.js
/waitForElement.js
/mockApi.js
Каждый файл должен выполнять одну задачу и быть как можно более независимым от других частей проекта.
Переиспользование утилит в тестах с использованием React Testing Library позволяет значительно упростить процесс тестирования и повышает читаемость кода. Важно выделять общий функционал, который используется во многих тестах, и инкапсулировать его в удобные и абстрагированные утилиты. Это повышает поддерживаемость тестов и облегчает их расширение.