Given-When-Then соглашение

Тестирование является важной частью разработки программного обеспечения, и одной из распространенных методик является использование соглашения Given-When-Then. Эта структура описывает различные этапы выполнения тестов и помогает организовать тесты так, чтобы они были понятными и читаемыми. Применение этой структуры в тестах с React Testing Library позволяет улучшить качество тестирования компонентов и поведение приложения, делая тесты более логичными и структурированными.

Основные элементы

Given (Дано): Начальные условия для теста, которые описывают исходное состояние системы или компонента. Это включает подготовку всех данных, конфигураций и состояний, которые должны быть настроены перед выполнением действия.

When (Когда): Действие, которое инициирует изменение в состоянии системы. Это может быть взаимодействие пользователя, вызов функции или событие.

Then (Тогда): Ожидаемый результат, который должен быть проверен. Этот этап включает утверждения, которые подтверждают, что компонент или система ведет себя так, как ожидается, после выполнения действия.

Пример использования в React Testing Library

React Testing Library предоставляет удобные утилиты для работы с DOM и тестирования компонентов. Структура Given-When-Then помогает организовать тесты, улучшая их читаемость и поддержку. Рассмотрим пример, в котором тестируется компонент кнопки, меняющей текст при клике.

import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';  // Предположим, что это наш компонент

test('Меняет текст при клике', () => {
  // Given: начальные условия
  render(<Button initialText="Нажми меня" />);

  // When: действие
  fireEvent.click(screen.getByText(/Нажми меня/i));

  // Then: ожидание
  expect(screen.getByText(/Кликнуто/i)).toBeInTheDocument();
});

В этом тесте:

  1. Given: Мы рендерим компонент с начальным текстом “Нажми меня”.
  2. When: Пользователь кликает на кнопку.
  3. Then: Ожидаем, что текст кнопки изменится на “Кликнуто”.

Преимущества применения Given-When-Then

  1. Читаемость: Разделение теста на три логические части помогает легко понять, что происходит в каждом тесте. Это значительно улучшает восприятие кода, особенно когда тесты становятся более сложными.
  2. Поддерживаемость: Структурированные тесты проще изменять и расширять. Когда структура теста ясна, проще найти, где возникает ошибка, и понять, какие условия необходимо изменить.
  3. Повторное использование: Логика “Given” и “When” может быть повторно использована в других тестах, что упрощает создание новых тестов на основе существующих.

Сложные примеры с несколькими шагами

Применение Given-When-Then становится особенно полезным, когда тесты включают несколько действий или сложные взаимодействия с компонентами. Рассмотрим пример, когда компонент имеет форму с несколькими полями, и необходимо проверить логику отправки формы.

import { render, screen, fireEvent } from '@testing-library/react';
import Form from './Form';  // Форма с полями имени и email

test('Заполнение формы и отправка', () => {
  // Given: начальные условия
  render(<Form />);

  // When: пользователь заполняет форму
  fireEvent.change(screen.getByLabelText(/Имя/i), { target: { value: 'Иван' } });
  fireEvent.change(screen.getByLabelText(/Email/i), { target: { value: 'ivan@example.com' } });
  fireEvent.click(screen.getByText(/Отправить/i));

  // Then: проверка отправки формы
  expect(screen.getByText(/Форма отправлена/i)).toBeInTheDocument();
});

Здесь тест состоит из нескольких шагов:

  1. Given: Рендерим форму.
  2. When: Заполняем поля формы и отправляем ее.
  3. Then: Проверяем, что форма была успешно отправлена.

Ожидания и асинхронные тесты

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

Пример с асинхронной загрузкой данных:

import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import FetchDataButton from './FetchDataButton';  // Кнопка для загрузки данных

test('Загружает данные при клике', async () => {
  // Given: начальные условия
  render(<FetchDataButton />);

  // When: клик по кнопке загрузки данных
  fireEvent.click(screen.getByText(/Загрузить данные/i));

  // Then: ожидаем, что данные появятся на экране
  await waitFor(() => expect(screen.getByText(/Данные загружены/i)).toBeInTheDocument());
});

В этом примере:

  1. Given: Мы рендерим кнопку для загрузки данных.
  2. When: Пользователь кликает на кнопку.
  3. Then: После того как данные загружены, мы проверяем, что текст “Данные загружены” отображается на экране. Здесь мы используем waitFor для ожидания асинхронной операции.

Тестирование с использованием моков

Тестирование компонентов, которые зависят от внешних сервисов или API, также может быть структурировано с использованием Given-When-Then. В таких случаях полезно использовать моки для имитации ответов от сервера.

import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import FetchDataButton from './FetchDataButton';

test('Загружает данные с сервера', async () => {
  const mock = new MockAdapter(axios);
  
  // Given: настройка мока для ответа от API
  mock.onGet('/data').reply(200, { message: 'Данные загружены' });

  render(<FetchDataButton />);

  // When: пользователь кликает для загрузки данных
  fireEvent.click(screen.getByText(/Загрузить данные/i));

  // Then: проверка, что данные были загружены
  await waitFor(() => expect(screen.getByText(/Данные загружены/i)).toBeInTheDocument());
});

Здесь мы мокируем ответ API, чтобы контролировать данные, которые будут возвращены, и используем структуру Given-When-Then для описания процесса тестирования.

Подытожим

Структура Given-When-Then является эффективным инструментом для организации тестов в React, особенно когда тестируем сложные компоненты с множеством состояний и взаимодействий. Правильное использование этой структуры делает тесты не только более читаемыми, но и удобными для поддержки. Важно помнить, что каждый элемент — Given, When и Then — играет свою роль в тестировании и помогает формировать логичную цепочку событий, улучшая процесс разработки и проверки компонентов.