В React компоненты часто изменяются в ответ на действия пользователя, такие как клики, ввод данных или асинхронные запросы. При тестировании таких компонентов важно учитывать добавление и удаление элементов в процессе работы. React Testing Library предоставляет набор инструментов для проверки этих динамических изменений.
В React элементы могут быть добавлены или удалены из DOM-дерева в зависимости от состояния компонента. React Testing Library ориентируется на тестирование того, как компоненты взаимодействуют с пользователем, а не на структуру DOM. Это позволяет тестировать компоненты на более высоком уровне, как если бы это был настоящий пользователь.
Чтобы проверить динамическое добавление или удаление элементов,
необходимо использовать подходящие методы, такие как getBy,
queryBy, findBy и их варианты для работы с
асинхронными событиями.
В следующем примере создается компонент, который добавляет новый элемент списка при нажатии на кнопку.
import { render, screen, fireEvent } from '@testing-library/react';
import React, { useState } from 'react';
function ItemList() {
const [items, setItems] = useState([]);
const addItem = () => setItems([...items, `Item ${items.length + 1}`]);
return (
<div>
<button onCl ick={addItem}>Добавить элемент</button>
<ul>
{items.map((item, index) => <li key={index}>{item}</li>)}
</ul>
</div>
);
}
test('добавление элемента в список при нажатии кнопки', () => {
render(<ItemList />);
const button = screen.getByText(/Добавить элемент/i);
fireEvent.click(button);
const newItem = screen.getByText(/Item 1/i);
expect(newItem).toBeInTheDocument();
});
В этом примере:
ItemList содержит кнопку, при нажатии на
которую в список добавляется новый элемент.getByText, чтобы найти кнопку и
элемент списка.fireEvent.click симулирует нажатие кнопки.toBeInTheDocument проверяет, что новый элемент
списка появился на экране.Компонент, который удаляет элемент из списка, может выглядеть следующим образом:
import { render, screen, fireEvent } from '@testing-library/react';
import React, { useState } from 'react';
function ItemList() {
const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3']);
const removeItem = (item) => {
setItems(items.filter((i) => i !== item));
};
return (
<div>
<ul>
{items.map((item) => (
<li key={item}>
{item}
<button onCl ick={() => removeItem(item)}>Удалить</button>
</li>
))}
</ul>
</div>
);
}
test('удаление элемента из списка при нажатии кнопки', () => {
render(<ItemList />);
const removeButton = screen.getByText(/Удалить/i);
fireEvent.click(removeButton);
const removedItem = screen.queryByText(/Item 1/i);
expect(removedItem).toBeNull();
});
В этом примере:
queryByText используется для поиска удаленного
элемента. Так как элемент должен быть удален, он не будет найден, и
toBeNull проверяет это.При тестировании компонентов, в которых добавление или удаление
элементов зависит от асинхронных операций (например, запросов к
серверу), следует использовать методы, которые позволяют работать с
элементами, появляющимися с задержкой. Для этого можно воспользоваться
методами findBy, findAllBy и их асинхронными
вариантами.
Пример компонента, где элементы добавляются после асинхронного запроса:
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import React, { useState, useEffect } from 'react';
function ItemList() {
const [items, setItems] = useState([]);
useEffect(() => {
const fetchItems = async () => {
const response = await fetch('/api/items');
const data = await response.json();
setItems(data);
};
fetchItems();
}, []);
return (
<div>
<ul>
{items.map((item) => <li key={item}>{item}</li>)}
</ul>
</div>
);
}
test('загрузка элементов при асинхронном запросе', async () => {
render(<ItemList />);
// Ждем, пока элементы появятся
const item = await screen.findByText(/Item 1/i);
expect(item).toBeInTheDocument();
});
В этом примере:
findByText используется для ожидания появления
элемента в DOM. Он автоматически ждет, пока элемент не будет
добавлен.При работе с динамическими элементами важно учитывать, что добавление
или удаление элементов может происходить с задержкой. Методы, такие как
findBy, findAllBy, и waitFor,
обеспечивают необходимую синхронизацию с DOM, чтобы избежать ошибок при
проверке элементов.
waitForМетод waitFor позволяет ожидать, пока определенное
условие не будет выполнено. Например, если необходимо удостовериться,
что элемент появился после некоторых изменений, можно использовать
следующий подход:
test('ожидание появления элемента после клика', async () => {
render(<ItemList />);
const button = screen.getByText(/Добавить элемент/i);
fireEvent.click(button);
await waitFor(() => screen.getByText(/Item 1/i));
const newItem = screen.getByText(/Item 1/i);
expect(newItem).toBeInTheDocument();
});
queryBy для удалённых элементовКогда элемент удаляется, важно проверять, что его больше нет в DOM.
Метод queryByText подходит для этого, так как он возвращает
null, если элемент не найден, в отличие от
getByText, который выбрасывает ошибку.
test('удаление элемента с использованием queryBy', async () => {
render(<ItemList />);
const button = screen.getByText(/Удалить/i);
fireEvent.click(button);
const removedItem = screen.queryByText(/Item 1/i);
expect(removedItem).toBeNull();
});
Тестирование динамического добавления и удаления элементов в
React-компонентах с помощью React Testing Library требует внимательного
подхода к асинхронным операциям и правильному выбору методов для
взаимодействия с DOM. Методы getBy, queryBy,
findBy, и waitFor позволяют создавать тесты,
которые точно отражают реальное поведение компонента в различных
сценариях.