Spec-Zone.ru › Jest

Тестирование снимков

Тесты снимков — очень полезный инструмент, когда необходимо убедиться, что ваш интерфейс пользователя (UI) не меняется неожиданно.

Типичный тестовый случай тестирования снимков отображает компонент пользовательского интерфейса, создает снимок, затем сравнивает его со справочным файлом снимка, хранящимся вместе с тестом. Тест завершится неудачей, если два снимка не совпадут: либо изменение неожиданное, либо справочный снимок нужно обновить до новой версии компонента пользовательского интерфейса.

Тестирование снимков с Jest

Аналогичный подход можно использовать при тестировании компонентов React. Вместо рендеринга графического пользовательского интерфейса, для чего потребовалось бы создание всего приложения, можно использовать тестовый рендерер для быстрого создания сериализуемого значения для вашего дерева React. Рассмотрите этот пример теста для компонента Link:

import renderer from 'react-test-renderer';
import Link from '../Link';

it('renders correctly', () => {
  const tree = renderer
    .create(<Link page="http://www.facebook.com">Facebook</Link>)
    .toJSON();
  expect(tree).toMatchSnapshot();
});

В первый раз при выполнении этого теста Jest создает файл снимка, который выглядит так:

exports[`renders correctly 1`] = `
<a
  className="normal"
  href="http://www.facebook.com"
  onMouseEnter={[Function]}
  onMouseLeave={[Function]}
>
  Facebook
</a>
`;

Файл снимка должен быть добавлен к изменениям кода и рассмотрен в процессе проверки кода. Jest использует pretty-format для того, чтобы снимки были удобочитаемыми во время проверки кода. При последующих запусках тестов Jest сравнит выходные данные рендеринга с предыдущим снимком. Если они совпадают, тест пройдёт. Если они не совпадают, это означает, что система обнаружила ошибку в вашем коде (в компоненте <Link> в этом случае), которую необходимо исправить, или реализация изменилась, и снимок нужно обновить.

Примечание: снимок непосредственно привязан к данным, которые вы отображаете — в нашем примере к компоненту <Link /> с пропсом page, переданным в него. Это подразумевает, что даже если в любом другом файле отсутствуют пропсы (например, App.js) в компоненте <Link />, тест всё равно пройдёт, так как тест не знает о использовании компонента <Link /> и он ограничен только компонентом Link.js. Кроме того, рендеринг того же компонента с различными пропсами в других тестах снимков не повлияет на первый, так как тесты не знают о существовании друг друга.

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

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

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

Теперь давайте поговорим о случае, когда тест снимка завершается неудачей из-за преднамеренного изменения реализации.

// Updated test case with a Link to a different address
it('renders correctly', () => {
  const tree = renderer
    .create(<Link page="http://www.instagram.com">Instagram</Link>)
    .toJSON();
  expect(tree).toMatchSnapshot();
});

В этом случае Jest выведет следующий результат:

Поскольку мы только что обновили наш компонент, чтобы он указывал на другой адрес, вполне разумно ожидать изменений в снимке для этого компонента. Тест нашего снимка завершается неудачей, поскольку снимок для нашего обновленного компонента больше не соответствует файлу снимка для этого тестового случая.

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

jest --updateSnapshot

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

Если вы хотите ограничить тестовые случаи снимков, которые будут повторно созданы, вы можете добавить дополнительный флаг --testNamePattern для повторной записи снимков только для тех тестов, которые соответствуют шаблону.

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

Взаимодействующий режим снимков

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

После перехода в интерактивный режим снимков Jest последовательно проведет вас по не пройденным снимкам и даст вам возможность проверить выходные данные.

Здесь вы можете выбрать обновление снимка или переход к следующему:

После завершения Jest предоставит сводку перед возвращением в режим слежения:

Встроенные снимки

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

Пример:

Сначала напишите тест, вызвав .toMatchInlineSnapshot() без аргументов:

it('renders correctly', () => {
  const tree = renderer
    .create(<Link page="https://example.com">Example Site</Link>)
    .toJSON();
  expect(tree).toMatchInlineSnapshot();
});

При следующем запуске Jest tree будет вычисляться и снимок будет записан в качестве аргумента для toMatchInlineSnapshot.

it('renders correctly', () => {
  const tree = renderer
    .create(<Link page="https://example.com">Example Site</Link>)
    .toJSON();
  expect(tree).toMatchInlineSnapshot(`
<a
  className="normal"
  href="https://example.com"
  onMouseEnter={[Function]}
  onMouseLeave={[Function]}
>
  Example Site
</a>
`);
});

Вот и всё! Вы даже можете обновить снимки с помощью --updateSnapshot или с использованием ключа u в режиме --watch.

По умолчанию Jest обрабатывает запись снимков в ваш исходный код. Однако, если в вашем проекте используется prettier, Jest обнаружит это и делегирует работу prettier (включая соблюдение вашей конфигурации).

Матчеры свойств

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

it('will fail every time', () => {
  const user = {
    createdAt: new Date(),
    id: Math.floor(Math.random() * 20),
    name: 'LeBron James',
  };

  expect(user).toMatchSnapshot();
});

// Snapshot
exports[`will fail every time 1`] = `
Object {
  "createdAt": 2018-05-19T23:36:09.816Z,
  "id": 3,
  "name": "LeBron James",
}
`;

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

it('will check the matchers and pass', () => {
  const user = {
    createdAt: new Date(),
    id: Math.floor(Math.random() * 20),
    name: 'LeBron James',
  };

  expect(user).toMatchSnapshot({
    createdAt: expect.any(Date),
    id: expect.any(Number),
  });
});

// Snapshot
exports[`will check the matchers and pass 1`] = `
Object {
  "createdAt": Any<Date>,
  "id": Any<Number>,
  "name": "LeBron James",
}
`;

Любое заданное значение, которое не является матчером, будет проверяться точно и сохраняться в файле снимка:

it('will check the values and pass', () => {
  const user = {
    createdAt: new Date(),
    name: 'Bond... James Bond',
  };

  expect(user).toMatchSnapshot({
    createdAt: expect.any(Date),
    name: 'Bond... James Bond',
  });
});

// Snapshot
exports[`will check the values and pass 1`] = `
Object {
  "createdAt": Any<Date>,
  "name": 'Bond... James Bond',
}
`;
совет

Если речь идет о строке, а не об объекте, то вам нужно самостоятельно заменить случайную часть этой строки перед тестированием снимка. Для этого можно использовать, например, replace() и регулярные выражения.

const randomNumber = Math.round(Math.random() * 100);
const stringWithRandomData = `<div id="${randomNumber}">Lorem ipsum</div>`;
const stringWithConstantData = stringWithRandomData.replace(/id="\d+"/, 123);
expect(stringWithConstantData).toMatchSnapshot();

Другой способ — создать заглушки для библиотеки, ответственной за генерацию случайной части кода, который вы делаете снимком.

Рекомендации по наилучшей практике

Снимки — это великолепный инструмент для выявления неожиданных изменений интерфейса в вашем приложении — будь то ответ API, пользовательский интерфейс, журналы или сообщения об ошибках. Как и любая стратегия тестирования, существуют некоторые лучшие практики, о которых вы должны знать, и правила, которым вы должны следовать, чтобы эффективно их использовать.

1. Считаем снимки кодом

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

Убедитесь, что ваши снимки удобочитаемы, сохраняя их краткими, сфокусированными и используя инструменты, которые навязывают эти стилистические соглашения.

Как упоминалось ранее, Jest использует pretty-format для удобочитаемости снимков, но вы можете найти полезными дополнительные инструменты, такие как eslint-plugin-jest с его параметром no-large-snapshots, или snapshot-diff с его функцией сравнения снимков компонентов, чтобы способствовать сохранению кратких и сфокусированных утверждений.

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

2. Тесты должны быть детерминированными

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

Например, если у вас есть компонент Clock, который использует Date.now(), снимок, созданный из этого компонента, будет разным каждый раз при запуске тестового случая. В этом случае мы можем создать заглушку для метода Date.now(), чтобы он возвращал согласованное значение каждый раз при запуске теста:

Date.now = jest.fn(() => 1482363367071);

Теперь каждый раз, когда запускается тестовый случай снимка, Date.now() будет возвращать 1482363367071 согласованно. Это приведет к тому, что для этого компонента будет генерироваться один и тот же снимок независимо от того, когда запускается тест.

3. Использование описательных имен снимков

Старайтесь всегда использовать описательные названия тестов и/или снимков для снимков. Лучшие названия описывают ожидаемое содержимое снимка. Это облегчает проверяющим проверку снимков во время проверки и позволяет любому понять, соответствует ли устаревший снимок правильному поведению до обновления.

Например, сравните:

exports[`<UserName /> should handle some test case`] = `null`;

exports[`<UserName /> should handle some other test case`] = `
<div>
  Alan Turing
</div>
`;

С:

exports[`<UserName /> should render null`] = `null`;

exports[`<UserName /> should render Alan Turing`] = `
<div>
  Alan Turing
</div>
`;

Так как последнее точно описывает, что ожидается в выводе, становится понятнее, когда это неверно:

exports[`<UserName /> should render null`] = `
<div>
  Alan Turing
</div>
`;

exports[`<UserName /> should render Alan Turing`] = `null`;

Часто задаваемые вопросы

Создаются ли снимки автоматически на системах непрерывной интеграции (CI)?

Нет, начиная с Jest 20, снимки в Jest не создаются автоматически, когда Jest выполняется в системе CI без явного указания --updateSnapshot. Ожидается, что все снимки являются частью кода, который выполняется на CI, и поскольку новые снимки автоматически проходят, они не должны проходить тест на CI. Рекомендуется всегда коммитить все снимки и хранить их в системе контроля версий.

Следует ли коммитить файлы снимков?

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

Работает ли тестирование снимков только с компонентами React?

React и React Native компоненты являются хорошим примером использования тестирования снимков. Однако снимки могут захватывать любые сериализуемые значения и должны использоваться всякий раз, когда цель состоит в проверке правильности вывода. Репозиторий Jest содержит множество примеров тестирования вывода самого Jest, вывода библиотеки утверждений Jest, а также сообщений об ошибках из различных частей кодовой базы Jest. Посмотрите пример снимков вывода CLI в репозитории Jest.

В чем разница между тестированием снимков и тестированием визуальных регрессий?

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

Заменяет ли тестирование снимков модульное тестирование?

Тестирование снимков — это всего лишь одно из более чем 20 утверждений, поставляемых с Jest. Цель тестирования снимков не в том, чтобы заменить существующие модульные тесты, а в том, чтобы предоставить дополнительную ценность и упростить тестирование. В некоторых сценариях тестирование снимков может потенциально устранить необходимость модульного тестирования для определенного набора функций (например, компонентов React), но они также могут работать вместе.

Какова производительность тестирования снимков с точки зрения скорости и размера генерируемых файлов?

Jest был переписан с учетом производительности, и тестирование снимков не является исключением. Поскольку снимки хранятся в текстовых файлах, этот способ тестирования быстрый и надежный. Jest генерирует новый файл для каждого файла теста, который вызывает toMatchSnapshot сопоставитель. Размер снимков довольно небольшой: для справки, размер всех файлов снимков в самой кодовой базе Jest составляет менее 300 КБ.

Как разрешить конфликты в файлах снимков?

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

Можно ли применять принципы разработки через тестирование с помощью тестирования снимков?

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

Работает ли покрытие кода с тестированием снимков?

Да, так же, как и с любым другим тестом.

© 2022 Facebook, Inc.
Licensed under the MIT License.
https://jestjs.io/docs/snapshot-testing

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API