Стратегия постепенной миграции

Vue Test Utils (VTU) — официальная библиотека для юнит-тестирования компонентов Vue.js. Она обеспечивает изоляцию компонентов, управление их состоянием и удобный доступ к DOM через обертку компонента (wrapper). Основные функции VTU включают: рендеринг компонентов (mount и shallowMount), взаимодействие с их пропсами и событиями, а также асинхронное тестирование с реактивностью Vue.

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

Пример базового рендеринга:

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

const wrapper = mount(MyComponent, {
  props: { title: 'Тест' }
})

expect(wrapper.text()).toContain('Тест')

Работа с пропсами, слотами и событиями

Пропсы передаются через объект props при монтировании:

const wrapper = mount(MyComponent, {
  props: { count: 5 }
})
expect(wrapper.props('count')).toBe(5)

Слоты позволяют тестировать вставку пользовательского контента:

const wrapper = mount(MyComponent, {
  slots: { default: '<p>Слотовый текст</p>' }
})
expect(wrapper.html()).toContain('Слотовый текст')

Эмит события проверяются через метод emitted():

wrapper.vm.$emit('custom-event', 42)
expect(wrapper.emitted('custom-event')[0]).toEqual([42])

Асинхронность и реактивность

Компоненты Vue используют реактивные данные, поэтому тесты часто требуют ожидания обновления DOM после изменения состояния. VTU предоставляет nextTick из Vue:

import { nextTick } from 'vue'

wrapper.setData({ message: 'Новое сообщение' })
await nextTick()
expect(wrapper.text()).toContain('Новое сообщение')

Для промисов и асинхронных вызовов можно использовать await wrapper.vm.$nextTick() или flushPromises из вспомогательных библиотек.

Монтирование и изоляция компонентов

Для постепенной миграции старого кода на Vue 3 полезно использовать шаговую стратегию тестирования:

  1. Тестирование старых компонентов без изменений. Используется shallowMount для игнорирования сложных дочерних компонентов.
  2. Постепенная замена дочерних компонентов на реальные версии, чтобы убедиться, что интеграция не ломает функциональность.
  3. Полное монтирование нового компонента только после проверки стабильности каждого шага.

Пример изоляции дочернего компонента:

const wrapper = shallowMount(ParentComponent, {
  global: {
    stubs: ['ChildComponent'] // заменяет реальный компонент заглушкой
  }
})
expect(wrapper.findComponent({ name: 'ChildComponent' }).exists()).toBe(true)

Использование глобальных конфигураций и плагинов

VTU позволяет подключать плагины Vue и глобальные компоненты через объект global. Это особенно важно при миграции проектов с Vue 2 на Vue 3, когда используются сторонние библиотеки:

import { createStore } from 'vuex'

const store = createStore({ state: { count: 0 } })

const wrapper = mount(MyComponent, {
  global: { plugins: [store] }
})
expect(wrapper.vm.$store.state.count).toBe(0)

Рекомендации по стратегии постепенной миграции

  • Изоляция каждого компонента: минимизировать внешние зависимости и использовать заглушки.
  • Пошаговое тестирование интеграций: сначала пропсы и слоты, затем события и асинхронные реакции.
  • Контроль глобального состояния: подключать Vuex, Router или плагины только там, где это необходимо.
  • Проверка реактивности: после каждого изменения состояния использовать nextTick для проверки корректного обновления DOM.

Инструменты для упрощения перехода

  • shallowMount — для изоляции сложных компонентов.
  • flush-promises — для тестирования промисов и асинхронных вызовов.
  • stubs и mocks — для имитации глобальных компонентов и функций.
  • nextTick и await wrapper.vm.$nextTick() — для корректной проверки реактивных изменений.

Стратегия постепенной миграции с Vue Test Utils позволяет минимизировать риск регрессий, точно контролировать поведение компонентов и последовательно интегрировать новые версии компонентов и библиотек.