Создание снимков (toMatchSnapshot)

Снимковое тестирование (snapshot testing) фиксирует текущее состояние компонента и сравнивает его с сохранённым эталоном при последующих запусках тестов. В экосистеме Vue основным инструментом для этого выступает связка Vue Test Utils + Jest, где ключевую роль играет матчер toMatchSnapshot.

Снимок представляет собой сериализованное описание результата: HTML-разметки, виртуального DOM, структуры данных или объекта. При первом запуске теста снимок сохраняется в файл. При следующих запусках результат теста сравнивается с сохранённой версией.


Подключение и базовый принцип работы

toMatchSnapshot предоставляется Jest и не требует дополнительной настройки при стандартной конфигурации тестовой среды.

Простейший пример:

import { mount } from '@vue/test-utils'
import Button from '@/components/Button.vue'

test('рендер кнопки соответствует снимку', () => {
  const wrapper = mount(Button)
  expect(wrapper.html()).toMatchSnapshot()
})

Алгоритм работы:

  1. Компонент монтируется.
  2. Получается HTML-разметка через wrapper.html().
  3. Jest сериализует строку и сравнивает с сохранённым снимком.
  4. При отсутствии снимка он создаётся автоматически.

Формат и хранение снимков

Снимки сохраняются в директории __snapshots__, рядом с файлом теста:

Button.spec.js
__snapshots__/
  Button.spec.js.snap

Содержимое файла .snap:

exports[`рендер кнопки соответствует снимку 1`] = `
<button class="btn btn-primary">
  Нажми меня
</button>
`

Каждый снимок связан с конкретным тестом и имеет уникальный ключ.


Снимки Vue-компонентов: html vs vnode

Снимок HTML-разметки

Наиболее распространённый вариант:

expect(wrapper.html()).toMatchSnapshot()

Преимущества:

  • Читаемость
  • Минимальный уровень шума
  • Удобно отслеживать изменения верстки

Недостатки:

  • Не отражает структуру виртуального DOM
  • Потеря информации о слотах и реактивности

Снимок виртуального DOM

expect(wrapper.element).toMatchSnapshot()

или

expect(wrapper.vm.$.subTree).toMatchSnapshot()

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


Управление динамическими данными

Компоненты часто содержат нестабильные значения: id, даты, случайные числа. Такие данные делают снимки хрупкими.

Пример проблемы:

<span>{{ Date.now() }}</span>

Каждый запуск теста создаёт новый снимок.

Решения

Мокирование времени:

jest.useFakeTimers().setSystemTime(new Date('2024-01-01'))

Стабилизация данных через props:

mount(Component, {
  props: {
    timestamp: 1704067200000
  }
})

Предобработка HTML:

expect(
  wrapper.html().replace(/\d{13}/g, '[timestamp]')
).toMatchSnapshot()

Именованные снимки

По умолчанию Jest генерирует имя автоматически. Для сложных сценариев используется явное имя:

expect(wrapper.html()).toMatchSnapshot('кнопка в активном состоянии')

В файле снимков это отображается так:

exports[`кнопка в активном состоянии 1`] = `...`

Именованные снимки повышают читаемость и упрощают навигацию при ошибках.


Снимки состояний компонента

Снимковое тестирование особенно эффективно при проверке различных состояний:

test('disabled состояние', () => {
  const wrapper = mount(Button, {
    props: { disabled: true }
  })
  expect(wrapper.html()).toMatchSnapshot()
})

test('loading состояние', () => {
  const wrapper = mount(Button, {
    props: { loading: true }
  })
  expect(wrapper.html()).toMatchSnapshot()
})

Каждое состояние фиксируется как отдельный эталон.


Снимки и слоты

Vue Test Utils корректно сериализует слоты:

mount(Card, {
  slots: {
    default: '<p>Контент</p>',
    footer: '<button>OK</button>'
  }
})

Снимок отразит итоговую разметку с учётом всех слотов. Это позволяет отслеживать изменения в шаблонах-обёртках и layout-компонентах.


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

При осознанных изменениях интерфейса старые снимки перестают совпадать.

Команда обновления:

jest -u

или

npm test -- -u

Jest перезаписывает все несовпадающие снимки. Использовать следует только после проверки изменений, так как обновление происходит без анализа причин расхождений.


Частые ошибки и анти-паттерны

Снимок всего приложения

  • Делает тесты громоздкими
  • Усложняет поиск причины ошибки

Снимки вместо логики

  • Не заменяют проверки условий, событий и вычислений
  • Не выявляют семантические ошибки

Частые автоматические обновления

  • Превращают тесты в формальность
  • Лишают снимки диагностической ценности

Лучшие практики

  • Делать снимки небольших, изолированных компонентов
  • Избегать нестабильных данных
  • Комбинировать снимки с явными expect
  • Использовать именованные снимки для сложных состояний
  • Проверять изменения перед обновлением

Комбинация снимков и пользовательских проверок

expect(wrapper.find('button').text()).toBe('Отправить')
expect(wrapper.html()).toMatchSnapshot()

Такой подход позволяет:

  • Явно проверять критичные элементы
  • Использовать снимок как дополнительную страховку

Роль снимков в поддерживаемых тестах

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