Тестирование манипуляций с DOM

Jest является популярным инструментом для тестирования JavaScript-программ, включая работу с DOM. Тестирование манипуляций с DOM важно для обеспечения правильности работы веб-приложений, особенно когда взаимодействие с элементами страницы является основным функционалом. В этой статье рассматриваются основные подходы и методы тестирования DOM в Jest.

Подготовка тестов

Перед тем как начать тестирование, необходимо настроить тестовую среду. Jest сам по себе не предоставляет полного API для манипуляций с DOM, однако в комбинации с библиотеками, такими как @testing-library/react, можно легко тестировать компоненты, взаимодействующие с DOM.

Для начала нужно установить необходимые пакеты:

npm install --save-dev @testing-library/react @testing-library/jest-dom

@testing-library/react предоставляет утилиты для работы с React-компонентами и их рендеринга, а @testing-library/jest-dom добавляет полезные матчеры для проверки DOM-элементов.

Рендеринг компонентов и создание тестов

Для тестирования манипуляций с DOM, первым шагом будет рендеринг компонента. Jest использует ReactDOM.render() или альтернативу, предоставляемую библиотеками, для рендеринга компонентов в виртуальный DOM. После этого можно выполнять тесты, проверяя, как изменения DOM влияют на отображение и поведение страницы.

Пример рендеринга компонента с использованием @testing-library/react:

import { render } from '@testing-library/react';
import MyComponent from './MyComponent';

test('renders component with text', () => {
  const { getByText } = render(<MyComponent />);
  const element = getByText(/Hello, World!/i);
  expect(element).toBeInTheDocument();
});

В этом примере мы рендерим компонент MyComponent и проверяем, что текст «Hello, World!» существует в DOM.

Взаимодействие с элементами DOM

Тестирование манипуляций с DOM часто включает симуляцию пользовательских действий, таких как клики, ввод текста и другие события. Для этого можно использовать утилиты из библиотеки @testing-library/react.

Для симуляции кликов или ввода данных применяются методы, такие как fireEvent. Рассмотрим пример, где мы тестируем кнопку, меняющую текст при клике:

import { render, fireEvent } from '@testing-library/react';
import Button from './Button';

test('changes text on click', () => {
  const { getByText } = render(<Button />);
  const button = getByText(/Click me/i);
  
  fireEvent.click(button);
  
  const newText = getByText(/Clicked!/i);
  expect(newText).toBeInTheDocument();
});

В этом примере мы симулируем клик по кнопке, что должно изменить текст на странице. После клика мы проверяем, что новый текст отображается в DOM.

Асинхронные манипуляции с DOM

Часто в реальных приложениях взаимодействие с DOM асинхронное, например, при загрузке данных с сервера. Для таких случаев используется findBy из @testing-library/react, который позволяет ожидать появления элемента через асинхронное событие.

Пример теста с асинхронным запросом:

import { render, screen, waitFor } from '@testing-library/react';
import UserProfile from './UserProfile';

test('loads and displays user data', async () => {
  render(<UserProfile />);
  
  // Ожидаем, что загрузка данных завершится и элемент появится
  const user = await screen.findByText(/John Doe/i);
  
  expect(user).toBeInTheDocument();
});

В этом примере мы тестируем компонент, который загружает данные о пользователе. Метод findByText ожидает появления элемента с текстом “John Doe” после асинхронной загрузки данных.

Очистка и работа с состоянием

Для корректной работы тестов важно очищать DOM после каждого теста, чтобы избежать конфликтов и утечек памяти. Jest автоматически очищает все изменения в DOM между тестами, но это можно также настроить вручную с помощью функции cleanup, которая вызывается после каждого теста.

import { render, cleanup } from '@testing-library/react';

afterEach(cleanup);

test('should render element correctly', () => {
  const { getByText } = render(<MyComponent />);
  const element = getByText(/Sample Text/i);
  expect(element).toBeInTheDocument();
});

Этот код позволяет гарантировать, что DOM очищается после каждого теста, предотвращая влияние одного теста на другой.

Использование Jest DOM матчеров

Jest DOM предоставляет набор матчеров, которые помогают проверять состояние DOM, такие как:

  • toBeInTheDocument(): проверяет, что элемент присутствует в DOM.
  • toHaveClass(): проверяет, что элемент имеет указанный CSS класс.
  • toHaveTextContent(): проверяет, что элемент содержит определённый текст.
  • toHaveAttribute(): проверяет наличие атрибута у элемента.

Пример использования матчеров:

import { render } from '@testing-library/react';
import Button from './Button';

test('button has correct text and class', () => {
  const { getByRole } = render(<Button />);
  const button = getByRole('button');
  
  expect(button).toHaveTextContent(/Click me/i);
  expect(button).toHaveClass('btn-primary');
});

В этом примере мы проверяем, что кнопка имеет правильный текст и класс.

Мокирование и манипуляции с событиями

В некоторых случаях важно протестировать обработку событий, таких как клик, изменение поля ввода и другие. Для этого часто используют мокирование функций. Jest позволяет замещать или отслеживать поведение функций через моки, что даёт возможность проверять, как компонент реагирует на различные события.

Пример тестирования обработчика клика с использованием моков:

import { render, fireEvent } from '@testing-library/react';
import Button from './Button';

test('calls handler on click', () => {
  const handleClick = jest.fn();
  const { getByText } = render(<Button onCl ick={handleClick} />);
  
  fireEvent.click(getByText(/Click me/i));
  
  expect(handleClick).toHaveBeenCalledTimes(1);
});

В этом примере мы тестируем, что функция обработчика клика была вызвана при нажатии на кнопку.

Тестирование компонентов с динамическими изменениями

Веб-приложения часто имеют динамически изменяющиеся элементы, такие как раскрывающиеся меню, модальные окна, всплывающие подсказки и другие UI-элементы. Для таких случаев важно тестировать, как состояние компонента влияет на отображение DOM.

Пример теста динамического компонента:

import { render, fireEvent } from '@testing-library/react';
import Dropdown from './Dropdown';

test('toggles dropdown visibility', () => {
  const { getByText, queryByText } = render(<Dropdown />);
  
  const button = getByText(/Toggle dropdown/i);
  fireEvent.click(button);
  
  const dropdownMenu = getByText(/Option 1/i);
  expect(dropdownMenu).toBeInTheDocument();
  
  fireEvent.click(button);
  expect(queryByText(/Option 1/i)).not.toBeInTheDocument();
});

Здесь проверяется, что при клике на кнопку скрывается и появляется выпадающее меню, что является частым элементом динамических изменений DOM.

Заключение

Тестирование манипуляций с DOM с использованием Jest и сопутствующих библиотек позволяет создавать надёжные и поддерживаемые тесты для веб-приложений. Основные принципы включают рендеринг компонентов, симуляцию событий, работу с асинхронными операциями и использование различных матчеров для проверки состояния DOM. Важно помнить о необходимости очистки DOM между тестами, а также о правильном мокировании функций для проверки событий.