Тестирование при помощи снимков
Тестирование с использованием снимков это очень полезный инструмент в ситуациях где вы хотите быть уверены, что ваш пользовательский интерфейс не изменяется неожиданным образом.
Типичный тест снимка для мобильного приложения отображает элемент пользовательского интерфейса, совершает снимок экрана и затем сравнивает его со связанным изображением хранимым наряду с тестом. Тест провалится если два этих изображения не совпадают: либо изменение непредвиденно, либо снимок экрана нуждается в обновлении для совпадения с новой версией элемента интерфейса.
Тестирование с использованием снимков в Jest #
Похожий подход может быть использован, когда дело доходит до тестирования React компонентов. Вместо отображения графического пользовательского интерфейса, что потребует сборки всего приложения, разработчик может использовать специализированный модуль для генерации сериализуемого значения для конкретного React дерева. Consider this example test for a simple Link component:
import React from 'react'; import Link from '../Link.react'; import renderer from 'react-test-renderer'; it('отображается корректно', () => { const tree = renderer.create( <Link page="http://www.facebook.com">Facebook</Link> ).toJSON(); expect(tree).toMatchSnapshot(); });
The first time this test is run, Jest creates a snapshot file that looks like this:
exports[`отображается корректно 1`] = ` <a className="normal" href="http://www.facebook.com" onMouseEnter={[Function]} onMouseLeave={[Function]} > Facebook </a> `;
Данный артефакт снимка следует занести в систему контроля версий наряду с изменениями в коде как часть вашего процесса по ревью кода. Jest использует pretty-format для того, чтобы сделать снимки подходящими для восприятия людьми во время процесса по ревью кода. При последующих запусках тестов Jest просто сравнит отображаемые выводом данные с предыдущим снимком. Если они совпадают, то тест успешно завершится. Если они не совпадают, то либо система тестирования обнаружила в вашем коде баг, который следует исправить, либо реализация изменилась и необходимо обновить снимок.
Более подробную информацию о том как тестирование с использованием снимков работает и почему мы ее реализовали можно найти в данной записи нашего блога. Ме рекомендуем ознакомиться со следующей записью, чтобы получить хорошее представление о том, когда использовать тестирование с использованием снимков. We also recommend watching this egghead video on Snapshot Testing with Jest.
Обновление снимков #
Если был введен баг, то довольно просто обнаружить тест со снимком, который проваливается. Когда такое происходит, устраните проблему и убедитесь, что ваши тесты завершаются успешно. А теперь давайте поговорим о случае, когда тест проваливается по причине осознанных изменений в реализации.
Подобная ситуация може возникнуть если мы намеренно внесем изменения в адрес ссылки внутри компонента Link в нашем примере.
// Добавлен кейс с Link, указывающей на другой адрес it('renders correctly', () => { const tree = renderer.create( <Link page="http://www.instagram.com">Instagram</Link> ).toJSON(); expect(tree).toMatchSnapshot(); });
В этом случае Jest выведет следующее сообщение:
Так как мы только что изменили наш компонент так, чтобы он указывал на другой адрес, то разумно ожидать изменений в снимке этого компонента. Наш тест с использованием снимка проваливается потому, что текущий снимок более не совпадает с сохраненным.
Чтобы решить эту проблему, нам нужно будет обновить сохраненный снимок. Вы можете запустить Jest с флагом, который укажет на необходимость повторной генерации снимков:
jest --updateSnapshot
Не ждите и примените внесенные изменения, выполнив команду указанную выше. Вы также можете использовать эквивалентный односимвольный флаг -u
для повторного создания снимков, если предпочитаете. Это создаст и сохранит новый снимок для всех проваливающихся тестов использующих снимки. Если бы существовали дополнительные проваливающиеся тесты ввиду непреднамеренных неполадок, нам бы понадобилось исправить неполадки перед обновлением снимков, чтобы избежать сохранения снимков с неполадками в поведении.
Если вы хотите ограничить то, какие снимки будут сохранены заново, вы можете указать дополнительный флаг --testNamePatter
для сохранения снимков лишь для тех тестов, которые совпадают с указанным шаблоном.
Вы можете опробовать данный функционал клонировав пример снимка, изменив компонент Link
и запустив Jest.
Тесты должны быть детерминированными #
Ваши тесты должны быть детерминированными. То есть, запуск одних и тех же тестов множество раз для компонента, который не менялся, должен возвращать одинаковые результаты каждый раз. Вы ответственны за убеждение в том, что сохраненные снимки не включают специфичные для платформы или любые другие недетерминированные данные.
Например если у вас есть компонент Clock, который использует Date.now()
, то снимок сгенерированный для этого компонента будет разным каждый раз, когда вы запускаете тест. В этом случае мы можем создать мок для метода Date.now(), чтобы возвращать фиксированное значение при каждом запуске теста:
Date.now = jest.fn(() => 1482363367071);
Теперь при каждом запуске теста, Date.now()
будет постоянно возвращать значение 1482363367071
. Это приведет к тому, что для данного компонента будет создаваться один и тот же снимок независимо от того, когда тест запускается.
В среде непрерывной интеграции (CI) снимки не создаются автоматически #
Начиная с Jest 20 снимки не создаются автоматически, если Jest запущен на CI без явного указания флага --updateSnapshot
. Ожидается, что снимки являются обязательной частью теста, выполняемого на CI, следовательно, если они отсутствуют, тест не должен проходить. Рекомендуется всегда включать снимки в коммит вместе с тестами и хранить в системе контроля версий.
Часто задаваемые вопросы #
Нужно ли коммитить файлы со снимками? #
Yes, all snapshot files should be committed alongside the modules they are covering and their tests. They should be considered as part of a test, similar to the value of any other assertion in Jest. In fact, snapshots represent the state of the source modules at any given point in time. In this way, when the source modules are modified, Jest can tell what changed from the previous version. It can also provide a lot of additional context during code review in which reviewers can study your changes better.
Снимки работают только в тестах с React-компонентами? #
Компоненты React и React Native – хорошие примеры того, где можно применять тесты со снимками. Однако, снимки могут содержать любые сериализуемые данные, и их всегда следует использовать, если цель теста – проверка корректности вывода. В репозитории Jest есть множество примеров тестирования вывода Jest, встроенной в него библиотеки assert'ов, а также логирования событий в различных частях кодовой базы Jest. See an example of snapshotting CLI output in the Jest repo.
What's the difference between snapshot testing and visual regression testing? #
Snapshot testing and visual regression testing are two distinct ways of testing UIs, and they serve different purposes. Visual regression testing tools take screenshots of web pages and compare the resulting images pixel by pixel. With Snapshot testing values are serialized, stored within text files and compared using a diff algorithm. There are different trade-offs to consider and we listed the reasons why snapshot testing was built in the Jest blog.
Does snapshot testing substitute unit testing? #
Snapshot testing is only one of more than 20 assertions that ship with Jest. The aim of snapshot testing is not to replace existing unit tests, but providing additional value and making testing painless. In some scenarios, snapshot testing can potentially remove the need for unit testing for a particular set of functionalities (e.g. React components), but they can work together as well.
What is the performance of snapshot testing regarding speed and size of the generated files? #
Jest has been rewritten with performance in mind, and snapshot testing is not an exception. Since snapshots are stored within text files, this way of testing is fast and reliable. Jest generates a new file for each test file that invokes the toMatchSnapshot
matcher. The size of the snapshots is pretty small: For reference, the size of all snapshot files in the Jest codebase itself is less than 300 KB.
How do I resolve conflicts within snapshot files? #
Snapshot files must always represent the current state of the modules they are covering. Therefore, if you are merging two branches and encounter a conflict in the snapshot files, you can either resolve the conflict manually or to update the snapshot file by running Jest and inspecting the result.
Is it possible to apply test-driven development principles with snapshot testing? #
Although it is possible to write snapshot files manually, that is usually not approachable. Snapshots help figuring out whether the output of the modules covered by tests is changed, rather than giving guidance to design the code in the first place.
Does code coverage work with snapshots testing? #
Yes, just like with any other test.