Кэширование в тестах

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

Принципы кэширования

Кэширование в контексте тестирования Vue-компонентов заключается в сохранении результатов выполнения определённых операций для последующего повторного использования. Основные сценарии применения:

  • Рендеринг компонентов: если один и тот же компонент используется в нескольких тестах с одинаковыми пропсами и состоянием, можно сохранять его смонтированную версию в переменной.
  • Селекторы и поиск элементов: часто выполняемый поиск по DOM (wrapper.find, wrapper.findAll) можно закэшировать для многократного использования.
  • Вызовы методов и API: мокирование сетевых запросов или вычисляемых свойств с сохранением результата позволяет ускорить тесты и сделать их детерминированными.

Кэширование смонтированных компонентов

Для компонентов, рендер которых является дорогой операцией, применяется хранение wrapper в локальной области видимости тестового файла:

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

let wrapper

beforeAll(() => {
  wrapper = mount(MyComponent, {
    props: { initialCount: 5 }
  })
})

afterAll(() => {
  wrapper.unmount()
})

test('проверка текста', () => {
  expect(wrapper.text()).toContain('5')
})

test('проверка кнопки', () => {
  const button = wrapper.find('button')
  button.trigger('click')
  expect(wrapper.text()).toContain('6')
})

Использование beforeAll вместо beforeEach предотвращает многократное создание экземпляра компонента, что особенно важно при тяжелых или асинхронных компонентах.

Кэширование результатов поиска элементов

Vue Test Utils предоставляет удобные методы для поиска элементов, но многократное обращение к DOM может быть избыточным. Для повышения эффективности можно кэшировать результаты поиска:

let wrapper
let button

beforeEach(() => {
  wrapper = mount(MyComponent)
  button = wrapper.find('button')
})

test('клик увеличивает счётчик', async () => {
  await button.trigger('click')
  expect(wrapper.text()).toContain('1')
})

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

const cache = {}

beforeEach(() => {
  const wrapper = mount(MyComponent)
  cache.wrapper = wrapper
  cache.button = wrapper.find('button')
})

Кэширование моков и вычислений

Асинхронные операции или вычисляемые свойства могут быть дорогими для повторного выполнения. В тестах можно сохранять их значения:

import { vi } from 'vitest'

const fetchData = vi.fn(async () => {
  return Promise.resolve({ value: 42 })
})

let cachedData

beforeAll(async () => {
  cachedData = await fetchData()
})

test('значение кэшированного ответа', () => {
  expect(cachedData.value).toBe(42)
})

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

Взаимодействие кэша с реактивностью

Vue использует реактивность для отслеживания изменений в данных. При кэшировании компонентов или элементов важно учитывать, что состояние wrapper сохраняется вместе с реактивными свойствами. Для предотвращения нежелательных эффектов между тестами:

  • Использовать beforeAll только для неизменяемых состояний.
  • Для тестов с изменяемым состоянием лучше применять beforeEach, создавая свежий wrapper.
  • Сброс реактивного состояния через методы компонента или wrapper.setProps, wrapper.setData.
beforeEach(() => {
  wrapper.setData({ count: 0 }) // сброс состояния
})

Кэширование с shallowMount

Для оптимизации больших компонентов используется shallowMount, который рендерит компонент без дочерних компонентов. Кэширование shallowMount особенно эффективно при тестировании функционала родителя без необходимости рендерить сложные дочерние компоненты.

import { shallowMount } from '@vue/test-utils'
import ParentComponent from '@/components/ParentComponent.vue'

let wrapper

beforeAll(() => {
  wrapper = shallowMount(ParentComponent)
})

test('рендерим родителя', () => {
  expect(wrapper.exists()).toBe(true)
})

Советы по эффективному кэшированию

  • Сохранять в кэше только неизменяемые состояния.
  • Использовать afterAll для очистки кэша и размонтирования компонентов.
  • Избегать кэширования, если тесты проверяют реактивные изменения, иначе это приведет к ложноположительным результатам.
  • Для сложных тестов можно создавать отдельный объект cache с разными уровнями: компоненты, элементы, данные, моки.

Кэширование в тестах Vue Test Utils позволяет повысить скорость, уменьшить нагрузку на систему и сделать тесты более детерминированными при правильной организации жизненного цикла компонентов и моков.