Одной из наиболее часто встречающихся задач при тестировании
компонентов React является рендеринг списков. Когда список отображается
на основе данных, часто используется метод map() для обхода
массива и отображения каждого элемента. При тестировании таких
компонентов важно правильно моделировать данные, проверять рендеринг
каждого элемента списка и взаимодействовать с ними, чтобы убедиться, что
приложение ведет себя как ожидается.
Метод map() является стандартным способом отображения
массивов в React. Например, если у нас есть список пользователей, мы
можем отобразить его следующим образом:
function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
Здесь users — это массив объектов, и мы отображаем
каждый элемент с помощью map(). Каждый элемент списка
требует уникального ключа (в данном случае user.id), чтобы
React мог эффективно обновлять и повторно использовать компоненты при
изменении состояния.
Для тестирования таких компонентов можно использовать React Testing
Library, которая предоставляет простые и мощные инструменты для
рендеринга компонентов и взаимодействия с ними. Рассмотрим, как
протестировать компонент UserList.
import { render, screen } from '@testing-library/react';
import UserList from './UserList';
test('отрисовывает список пользователей', () => {
const users = [
{ id: 1, name: 'Иван' },
{ id: 2, name: 'Мария' },
{ id: 3, name: 'Алексей' },
];
render(<UserList users={users} />);
// Проверяем, что все имена пользователей отрисованы
users.forEach(user => {
const userNameElement = screen.getByText(user.name);
expect(userNameElement).toBeInTheDocument();
});
});
Этот тест выполняет следующие шаги:
UserList, передавая ему массив
пользователей.screen.getByText() для поиска каждого имени
пользователя в документе.toBeInTheDocument().Иногда компоненты React выполняют асинхронные операции, такие как загрузка данных с сервера. В таких случаях, чтобы протестировать компонент, нужно использовать асинхронные методы библиотеки.
Пример с асинхронной загрузкой данных:
import { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users')
.then(response => response.json())
.then(data => setUsers(data));
}, []);
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
В тестах, если компонент выполняет асинхронные операции, нужно
дождаться рендеринга и убедиться, что данные загружены. Для этого
используется findByText и waitFor из React
Testing Library.
import { render, screen, waitFor } from '@testing-library/react';
import UserList from './UserList';
test('загружает и отображает пользователей', async () => {
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve([
{ id: 1, name: 'Иван' },
{ id: 2, name: 'Мария' },
]),
})
);
render(<UserList />);
await waitFor(() => screen.getByText('Иван'));
expect(screen.getByText('Иван')).toBeInTheDocument();
expect(screen.getByText('Мария')).toBeInTheDocument();
});
Здесь мы используем jest.fn() для создания заглушки для
функции fetch, имитируя асинхронный ответ от API. Затем
используем waitFor для ожидания, пока элементы не появятся
в документе. Это важно для асинхронных операций, поскольку они могут
занять некоторое время для выполнения.
При тестировании рендеринга списка можно проверить не только наличие элементов, но и их порядок, правильность значений и атрибутов.
Например, для проверки порядка рендеринга:
test('пользователи отображаются в правильном порядке', () => {
const users = [
{ id: 1, name: 'Иван' },
{ id: 2, name: 'Мария' },
];
render(<UserList users={users} />);
const listItems = screen.getAllByRole('listitem');
expect(listItems[0]).toHaveTextContent('Иван');
expect(listItems[1]).toHaveTextContent('Мария');
});
В этом тесте мы получаем все элементы списка с помощью
screen.getAllByRole('listitem'), а затем проверяем, что
каждый элемент имеет ожидаемый текст. Также можно использовать
toHaveTextContent, чтобы проверить содержимое
элементов.
Ситуация с пустым списком также требует проверки. Важно убедиться, что компонент правильно отображает пустой список, когда данные не переданы.
Пример:
test('отображает сообщение о пустом списке, если нет пользователей', () => {
const users = [];
render(<UserList users={users} />);
const emptyMessage = screen.getByText('Нет пользователей');
expect(emptyMessage).toBeInTheDocument();
});
Здесь тестируется случай, когда список пуст, и нужно проверить, что компонент отображает сообщение “Нет пользователей”.
React Testing Library также позволяет тестировать взаимодействие с элементами, например, обработку кликов по элементам списка. В случае с компонентом списка это может быть важно, если элементы списка кликабельны.
import userEvent from '@testing-library/user-event';
test('при клике на пользователя вызывается обработчик', () => {
const users = [{ id: 1, name: 'Иван' }];
const onUserCl ick = jest.fn();
render(<UserList users={users} onUserCl ick={onUserClick} />);
const userElement = screen.getByText('Иван');
userEvent.click(userElement);
expect(onUserClick).toHaveBeenCalledWith(1);
});
В этом примере мы имитируем клик по элементу списка с помощью
библиотеки user-event. При клике должен вызываться
переданный в компонент обработчик, и тест проверяет, был ли вызван этот
обработчик с ожидаемым параметром.
Когда тестируем компоненты, использующие асинхронные данные, полезно мокировать запросы. Это позволяет избежать зависимости от внешних API и сделать тесты более стабильными.
Пример использования библиотеки msw для мокирования
запросов:
import { render, screen, waitFor } from '@testing-library/react';
import { server, rest } from 'msw';
import UserList from './UserList';
beforeAll(() => {
server.listen();
});
afterEach(() => {
server.resetHandlers();
});
afterAll(() => {
server.close();
});
test('загружает пользователей с мокированного API', async () => {
server.use(
rest.get('/api/users', (req, res, ctx) => {
return res(
ctx.json([
{ id: 1, name: 'Иван' },
{ id: 2, name: 'Мария' },
])
);
})
);
render(<UserList />);
await waitFor(() => screen.getByText('Иван'));
expect(screen.getByText('Иван')).toBeInTheDocument();
});
Здесь используется библиотека msw для мокирования
HTTP-запросов, позволяя имитировать ответы от API без реальной работы с
сервером.
Тестирование рендеринга списков с использованием метода
map() в React требует правильного подхода к моделированию
данных, ожиданию асинхронных операций и проверке взаимодействий с
элементами. React Testing Library предоставляет простые, но мощные
инструменты для проверки, что компоненты рендерят элементы как
ожидается, и реагируют на действия пользователей.