Изменения в API между версиями

Vue Test Utils (VTU) — ключевой инструмент для юнит-тестирования компонентов Vue.js. С развитием Vue 3 и релиза Vue Test Utils версии 2 произошло несколько значительных изменений в API, которые влияют на подходы к тестированию, синтаксис вызовов и возможности работы с компонентами. Рассмотрим основные изменения подробно.


Mounting API: mount и shallowMount

В VTU v1 для Vue 2 существовали методы mount и shallowMount с простым набором опций. В версии 2 структура опций изменилась, появились новые возможности:

Ключевые изменения:

  • global объект В Vue Test Utils v2 большинство глобальных конфигураций (плагины, компоненты, директивы, миксины) теперь передаются через объект global:

    import { mount } from '@vue/test-utils'
    import MyComponent from '@/components/MyComponent.vue'
    import Vuex from 'vuex'
    
    const wrapper = mount(MyComponent, {
      global: {
        plugins: [Vuex],
        components: {
          CustomButton: MyButton
        },
        directives: {
          focus: {
            mounted(el) { el.focus() }
          }
        }
      }
    })
  • stubs внутри global Ранее stubs передавались напрямую в опции mount. Сейчас рекомендуется помещать их внутрь global для унифицированного доступа:

    const wrapper = mount(MyComponent, {
      global: {
        stubs: ['router-link']
      }
    })
  • mocks Имитации глобальных объектов, таких как $router и $route, теперь тоже находятся в global.mocks:

    const wrapper = mount(MyComponent, {
      global: {
        mocks: {
          $router: { push: jest.fn() },
          $route: { params: { id: 1 } }
        }
      }
    })

Работа с событиями

API работы с событиями в версии 2 стало более строгим:

  • trigger Метод trigger теперь возвращает промис, так как многие события могут быть асинхронными с Vue 3. Для правильного ожидания изменений в DOM необходимо использовать await:

    await wrapper.find('button').trigger('click')
  • События компонента emitted() продолжает работать, но с улучшенной типизацией:

    wrapper.vm.$emit('submit', { name: 'test' })
    expect(wrapper.emitted().submit[0]).toEqual([{ name: 'test' }])

Изменения в работе с пропсами и слотом

  • Пропсы В v2 пропсы можно передавать напрямую как объект, но теперь рекомендуется строго типизировать для Vue 3:

    const wrapper = mount(MyComponent, {
      props: { title: 'Пример' }
    })
  • Слоты Синтаксис слотов унифицирован с Vue 3:

    const wrapper = mount(MyComponent, {
      slots: {
        default: '<span>Текст слота</span>',
        header: '<h1>Заголовок</h1>'
      }
    })
  • Scoped слоты Поддержка функциональных слотов теперь полностью через функции:

    const wrapper = mount(MyComponent, {
      slots: {
        default: ({ text }) => `<div>${text}</div>`
      }
    })

Изменения в методах поиска элементов

  • find и findAll Методы остались, но теперь возвращают объект DOMWrapper с расширенным API, полностью совместимым с Vue 3 реактивностью:

    const button = wrapper.find('button')
    expect(button.exists()).toBe(true)
  • findComponent и findAllComponents Для поиска дочерних компонентов теперь рекомендуется использовать именно эти методы, а не селекторы по классу или тегу:

    const childWrapper = wrapper.findComponent({ name: 'ChildComponent' })

Новые возможности: асинхронные компоненты и Suspense

Vue 3 активно использует асинхронные компоненты и Suspense. VTU v2 обеспечивает поддержку тестирования таких компонентов:

  • Асинхронные компоненты:

    import AsyncComp from '@/components/AsyncComp.vue'
    
    const wrapper = mount(AsyncComp)
    await flushPromises() // ожидание рендеринга асинхронного компонента
  • flushPromises Метод из VTU или сторонней утилиты для ожидания завершения всех промисов в компоненте, необходим при асинхронном рендеринге.


Изменения в взаимодействии с реактивностью

  • vm и реактивные данные В VTU v2 wrapper.vm предоставляет доступ к реактивным свойствам компонента, но любые изменения нужно сопровождать await nextTick():

    wrapper.vm.count++
    await wrapper.vm.$nextTick()
    expect(wrapper.text()).toContain('1')
  • Реактивные props и watch Изменение пропсов через setProps стало асинхронным:

    await wrapper.setProps({ title: 'Новый заголовок' })
    expect(wrapper.text()).toContain('Новый заголовок')

Обновление хелперов и утилит

Некоторые утилиты из VTU v1 были удалены или изменены:

  • wrapper.html() и wrapper.text() остались без изменений, но возвращают актуальный DOM после асинхронных обновлений.
  • wrapper.setData() теперь работает только с реактивными свойствами Vue 3 и должна использоваться аккуратно.
  • Устаревшие методы setComputed и destroy были удалены.

Краткая таблица соответствия VTU v1 → VTU v2

VTU v1 (Vue 2) VTU v2 (Vue 3)
mount(Component, options) mount(Component, { global: { ... } })
shallowMount(Component) shallowMount(Component, { global: { ... } })
wrapper.setProps() асинхронный, требует await
wrapper.trigger() асинхронный, требует await
findComponent({ ref }) сохраняется, поддержка Composition API
setData() работает только с реактивными свойствами

Изменения в API Vue Test Utils v2 обеспечивают полную совместимость с Vue 3 и позволяют более строго и предсказуемо тестировать компоненты, особенно с учетом реактивной природы данных, асинхронных компонентов и глобальной конфигурации. Правильное использование новых опций global, асинхронных методов и строгой типизации пропсов становится ключевым при построении надежных тестов.