Arrange-Act-Assert паттерн

Паттерн Arrange-Act-Assert (AAA) представляет собой структуру для написания тестов, широко используемую при тестировании React-компонентов. Этот подход помогает сделать тесты более читабельными и понятными, улучшая их поддерживаемость и предсказуемость. В контексте использования React Testing Library паттерн помогает разделить процесс тестирования на три логические части, что способствует ясной и последовательной структуре тестов.

1. Arrange — Подготовка теста

На этом этапе происходит подготовка всех зависимостей и начальных условий для выполнения теста. Это может включать создание необходимых данных, настройку моков (mock) для внешних зависимостей, а также рендеринг компонентов с нужными пропсами.

Пример:

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

test('displays user name correctly', () => {
  // Arrange: создание моковых данных для пользователя
  const user = { name: 'Иван Иванов', age: 30 };

  // Рендерим компонент с использованием подготовленных данных
  render(<UserProfile user={user} />);
});

Здесь происходит создание объекта пользователя, который затем передается в компонент UserProfile. Этап Arrange нацелен на то, чтобы подготовить все необходимое для проверки функциональности компонента.

2. Act — Выполнение действия

Этот шаг включает в себя выполнение действий, которые нужно протестировать, такие как клик по кнопке, ввод текста в форму, изменение состояния и другие взаимодействия с компонентом. В React Testing Library для этого используются такие методы, как fireEvent и userEvent, которые имитируют пользовательские взаимодействия.

Пример:

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

test('calls onClick when button is clicked', () => {
  // Arrange
  const handleClick = jest.fn();
  render(<Button onCl ick={handleClick}>Click me</Button>);

  // Act: имитируем клик по кнопке
  fireEvent.click(screen.getByText('Click me'));

  // Assert: проверяем, что функция была вызвана
  expect(handleClick).toHaveBeenCalledTimes(1);
});

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

3. Assert — Проверка результата

После того как действие выполнено, на этапе Assert необходимо проверить, что результат тестируемого действия соответствует ожиданиям. Это может быть проверка изменений в UI, проверка вызова функций, состояния компонентов и т.д. В React Testing Library для утверждений часто используются методы типа getByText, getByRole, queryByText, а также различные асинхронные методы, такие как findByText.

Пример:

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

test('button displays "Clicked!" after click', () => {
  // Arrange
  render(<Button />);

  // Act: имитируем клик по кнопке
  fireEvent.click(screen.getByText('Click me'));

  // Assert: проверяем, что текст изменился
  expect(screen.getByText('Clicked!')).toBeInTheDocument();
});

Здесь после клика по кнопке происходит проверка, что текст кнопки изменился на “Clicked!”. Этап Assert позволяет убедиться, что поведение компонента соответствует ожиданиям.

Преимущества использования паттерна AAA

  1. Читаемость Структура Arrange-Act-Assert делает тесты легко читаемыми, так как каждый этап теста имеет свою четкую роль. Это помогает разработчикам быстро ориентироваться в коде тестов и понимать, что именно проверяется.

  2. Меньше дублирования Разделение на этапы помогает избежать дублирования кода, так как каждый из них отвечает за конкретную задачу. Например, если в нескольких тестах требуется один и тот же набор данных, их можно подготовить в разделе Arrange и повторно использовать.

  3. Простота отладки При соблюдении паттерна AAA проще локализовать ошибку в тесте. Если тест не проходит, всегда понятно, на каком из этапов возникла проблема — на этапе подготовки, при выполнении действия или при проверке результата.

Пример теста с использованием паттерна AAA

Пример полноценного теста с использованием Arrange-Act-Assert паттерна:

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

test('toggles visibility of text when button is clicked', () => {
  // Arrange: рендерим компонент с начальным состоянием
  render(<Toggle />);

  // Act: имитируем клик по кнопке
  fireEvent.click(screen.getByText('Show/Hide'));

  // Assert: проверяем, что текст теперь виден
  expect(screen.getByText('Hello, World!')).toBeInTheDocument();

  // Act: имитируем второй клик по кнопке
  fireEvent.click(screen.getByText('Show/Hide'));

  // Assert: проверяем, что текст снова скрыт
  expect(screen.queryByText('Hello, World!')).not.toBeInTheDocument();
});

В этом примере паттерн AAA используется для тестирования компонента Toggle, который меняет видимость текста. Сначала происходит рендер компонента, затем дважды вызывается клик по кнопке, и на каждом шаге проверяется правильность изменения состояния видимости текста.

Асинхронные тесты и AAA

Когда в тестируемом компоненте присутствуют асинхронные операции (например, запросы к серверу), подход к написанию тестов изменяется, но паттерн AAA остается актуальным. В таких случаях важно учитывать задержки в рендеринге или обновлении компонентов, для чего React Testing Library предоставляет методы findBy и waitFor, которые позволяют работать с асинхронными задачами.

Пример асинхронного теста:

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

test('displays data after fetching', async () => {
  // Arrange
  render(<FetchData />);

  // Act: имитируем клик для загрузки данных
  fireEvent.click(screen.getByText('Load Data'));

  // Assert: ожидаем появления текста после асинхронной загрузки
  await waitFor(() => screen.getByText('Data Loaded'));
  
  expect(screen.getByText('Data Loaded')).toBeInTheDocument();
});

Здесь используется await waitFor, чтобы дождаться завершения асинхронного действия и проверки наличия загруженных данных.

Заключение

Паттерн Arrange-Act-Assert предоставляет ясную структуру для написания тестов в React, делая их более удобочитаемыми и поддерживаемыми. Разделение теста на три логически связанные части помогает упрощать как написание, так и отладку тестов. Даже при асинхронных операциях этот подход сохраняет свою эффективность, улучшая качество и надежность тестов.