Снимки React компонентов

Снимки (snapshots) представляют собой способ зафиксировать состояние UI-компонента в момент тестирования, чтобы впоследствии можно было сравнивать изменения и обнаруживать непреднамеренные изменения в интерфейсе. Jest предоставляет встроенную поддержку для создания и проверки снимков, что позволяет быстро и эффективно тестировать React компоненты.

Как работает снимок?

При первом запуске тестов, когда создаётся снимок компонента, Jest сохраняет HTML-вывод компонента в файл. Этот файл хранится в каталоге __snapshots__ рядом с тестируемыми файлами. При последующих запусках Jest будет сравнивать текущий вывод компонента с сохранённым снимком. Если вывод совпадает, тест проходит успешно. В противном случае, тест считается не пройденным, и Jest сообщает о расхождении, предлагая обновить снимок.

Создание снимка компонента

Для создания снимка в Jest используется метод toMatchSnapshot(). Он применяется в тестах, где необходимо зафиксировать состояние компонента. Рассмотрим пример тестирования простого React компонента.

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

test('renders correctly', () => {
  const { asFragment } = render(<MyComponent />);
  expect(asFragment()).toMatchSnapshot();
});

В данном примере используется метод render из библиотеки @testing-library/react, который рендерит компонент в тестовой среде. Функция asFragment() позволяет получить “срез” DOM-дерева компонента, который затем передаётся в toMatchSnapshot(). При первом запуске теста Jest создаст снимок текущего состояния компонента.

Обновление снимков

Когда разработчик вносит изменения в компонент, которые должны отразиться на его UI, необходимо обновить снимки. Jest предоставляет команду для автоматического обновления снимков:

jest --updateSnapshot

Это позволяет синхронизировать тесты с актуальными изменениями в компонентах, если изменения в UI действительно ожидаемы. В случае, если снимок был изменён случайным образом или ошибка в компоненте привела к нежеланным изменениям, необходимо отклонить обновление снимка и исправить проблему.

Структура снимков

Снимки хранятся в отдельных файлах в каталоге __snapshots__. Каждый файл снимков соответствует тесту и имеет структуру, подобную следующей:

__snapshots__/
  MyComponent.test.js.snap

Внутри файла хранится зафиксированный HTML-код компонента в виде строки, которая используется для сравнения с текущим состоянием компонента.

Пример содержимого файла снимка:

exports[`MyComponent renders correctly`] = `
<div>
  <h1>Hello, world!</h1>
  <p>Welcome to the React world!</p>
</div>
`;

Снимки и асинхронные компоненты

Тестирование асинхронных компонентов в Jest с использованием снимков требует дополнительных настроек. Например, если компонент загружает данные с сервера, необходимо дождаться их загрузки перед созданием снимка. Для этого можно использовать waitFor из библиотеки @testing-library/react или act() из React.

Пример теста для асинхронного компонента:

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

test('renders correctly after data is fetched', async () => {
  render(<MyAsyncComponent />);

  await waitFor(() => screen.getByText(/Data loaded/));

  expect(screen.getByText(/Data loaded/)).toMatchSnapshot();
});

В этом примере тестируется компонент, который асинхронно загружает данные. Сначала рендерится компонент, затем тест ждёт появления элемента с текстом “Data loaded”, после чего создаётся снимок.

Параметризация снимков

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

Пример:

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

test('renders correctly with different props', () => {
  const { asFragment } = render(<MyComponent title="Title 1" />);
  expect(asFragment()).toMatchSnapshot();

  const { asFragment: asFragment2 } = render(<MyComponent title="Title 2" />);
  expect(asFragment2()).toMatchSnapshot();
});

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

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

Снимки могут быть полезны не только для проверки UI-компонентов, но и для тестирования производительности. Например, можно фиксировать рендеринг компонента в различных условиях и сравнивать скорость рендеринга с предыдущими версиями. Для этого можно использовать методы времени из стандартной библиотеки JavaScript, такие как performance.now().

Пример:

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

test('measures render time', () => {
  const startTime = performance.now();
  render(<MyComponent />);
  const endTime = performance.now();
  const renderTime = endTime - startTime;

  expect(renderTime).toBeLessThan(100); // Ожидаем, что рендеринг займет менее 100 мс
});

Советы по использованию снимков

  1. Минимизация изменений: Избегайте внесения изменений в компоненты, которые не влияют на функциональность или внешний вид. Это поможет избежать лишних обновлений снимков.
  2. Частые обновления снимков: Когда изменения в UI компонентах всё же неизбежны, обновляйте снимки как можно скорее, чтобы тесты не показывали ложных срабатываний.
  3. Не используйте снимки для тестирования логики: Снимки идеально подходят для тестирования UI, но не следует использовать их для проверки бизнес-логики. Для логики предпочтительнее использовать другие подходы, такие как юнит-тесты.
  4. Ограничение использования снимков: Использование снимков должно быть разумным. Слишком большое количество снимков может привести к сложности в поддержке тестов, особенно если компонент сильно изменяется.

Проблемы и их решение

  1. Частые ложные срабатывания: Ложные срабатывания могут происходить, если в компоненте изменяются мелкие детали, не влияющие на функциональность. В таких случаях можно использовать toMatchInlineSnapshot(), чтобы вручную фиксировать снимки прямо в тестах.

  2. Большие снимки: Снимки, содержащие огромные объёмы данных, могут быть неудобными для поддержки. Один из способов решения этой проблемы — использование более мелких, изолированных компонентов, которые тестируются отдельно.

Итоги

Снимки в Jest — это мощный инструмент для тестирования React компонентов, который позволяет фиксировать их визуальное состояние и проверять, не произошли ли непреднамеренные изменения. Этот метод особенно полезен для UI-компонентов, но также требует внимательности при обновлении снимков и при работе с асинхронными или параметризованными компонентами.