Добавление и удаление элементов динамически

В 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 позволяют создавать тесты, которые точно отражают реальное поведение компонента в различных сценариях.