Тестирование с Vitest

Vitest — современная тестовая библиотека для проектов на Vue.js и Vuetify, ориентированная на скорость и интеграцию с современными сборщиками, такими как Vite. Она сочетает возможности Jest с легкостью конфигурации и быстродействием. В контексте Vuetify особое внимание уделяется тестированию компонентов интерфейса, их реактивности, событий и взаимодействий с DOM.

Установка и настройка Vitest

Для начала необходимо установить Vitest и сопутствующие пакеты:

npm install -D vitest @vue/test-utils vue

Если проект использует Vuetify, подключение также потребует самого Vuetify:

npm install vuetify@next

Создание конфигурационного файла vitest.config.js:

import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './vitest.setup.js'
  }
})

В файле vitest.setup.js можно инициализировать Vuetify для тестов:

import { createVuetify } from 'vuetify'
import { config } from '@vue/test-utils'

const vuetify = createVuetify()

config.global.plugins = [vuetify]

Организация тестов компонентов

Для Vuetify-компонентов важна изоляция и проверка их визуальной и функциональной составляющей. Vitest совместно с @vue/test-utils позволяет рендерить компонент в виртуальном DOM и взаимодействовать с ним программно.

Пример теста простого компонента VBtn:

import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import { VBtn } from 'vuetify/components'

describe('VBtn', () => {
  it('рендерит текст кнопки', () => {
    const wrapper = mount(VBtn, {
      slots: { default: 'Нажми меня' }
    })
    expect(wrapper.text()).toBe('Нажми меня')
  })

  it('генерирует событие click', async () => {
    const wrapper = mount(VBtn)
    await wrapper.trigger('click')
    expect(wrapper.emitted()).toHaveProperty('click')
  })
})

Ключевой момент — использование mount для полного рендеринга компонента, что особенно важно для Vuetify, где компоненты зависят от глобальных стилей и контекста темы.

Мокирование Vuetify-плагинов и глобальных функций

Vuetify часто использует плагины и глобальные функции, такие как $vuetify или темы. Для корректного тестирования их можно замокировать:

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

const globalMock = {
  $vuetify: {
    theme: { dark: false }
  }
}

const wrapper = mount(MyComponent, {
  global: {
    mocks: globalMock
  }
})

Это позволяет проверить реакцию компонента на изменение темы или других глобальных состояний без необходимости реальной инициализации всего Vuetify.

Тестирование взаимодействия с формами

Vuetify предоставляет множество формовых компонентов: VTextField, VSelect, VCheckbox. Их тестирование требует проверки как визуального рендеринга, так и реактивного поведения.

Пример теста VTextField:

import { mount } from '@vue/test-utils'
import { VTextField } from 'vuetify/components'

describe('VTextField', () => {
  it('принимает ввод пользователя', async () => {
    const wrapper = mount(VTextField, {
      props: { modelValue: '' }
    })

    await wrapper.setValue('Привет')
    expect(wrapper.props('modelValue')).toBe('Привет')
    expect(wrapper.find('input').element.value).toBe('Привет')
  })
})

Особенность Vuetify в том, что многие компоненты используют слоты и реактивные свойства v-model, поэтому важно тестировать через setValue и проверять как свойства компонента, так и фактический DOM.

Снапшоты и визуальная стабильность

Vitest поддерживает снапшот-тестирование, что особенно полезно для сложных интерфейсных компонентов Vuetify:

import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import MyCard from './MyCard.vue'

describe('MyCard', () => {
  it('совпадает со снапшотом', () => {
    const wrapper = mount(MyCard)
    expect(wrapper.html()).toMatchSnapshot()
  })
})

Это позволяет отслеживать изменения в структуре компонента и предотвращать случайные визуальные регрессии.

Асинхронные действия и события

Многие Vuetify-компоненты запускают асинхронные действия при взаимодействии (например, открытие VDialog). Vitest поддерживает асинхронное тестирование с помощью async/await:

import { mount } from '@vue/test-utils'
import { VDialog, VBtn } from 'vuetify/components'

describe('VDialog', () => {
  it('открывает диалог по кнопке', async () => {
    const wrapper = mount({
      template: `
        <v-dialog v-model="open">
          <template #activator="{ on }">
            <v-btn v-on="on">Открыть</v-btn>
          </template>
          <div>Содержимое диалога</div>
        </v-dialog>
      `,
      data: () => ({ open: false })
    })

    await wrapper.findComponent(VBtn).trigger('click')
    expect(wrapper.vm.open).toBe(true)
  })
})

Здесь важно тестировать через реальный DOM и события Vuetify, поскольку многие компоненты используют внутренние обработчики и слоты-активаторы.

Интеграционное тестирование с Vitest

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

Пример интеграции нескольких компонентов с состоянием:

import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import MyForm from './MyForm.vue'
import { createVuetify } from 'vuetify'

const vuetify = createVuetify()

describe('MyForm', () => {
  it('отправляет данные после заполнения всех полей', async () => {
    const wrapper = mount(MyForm, {
      global: { plugins: [vuetify] }
    })

    await wrapper.find('input[name="name"]').setValue('Иван')
    await wrapper.find('input[name="email"]').setValue('ivan@example.com')
    await wrapper.find('form').trigger('submit.prevent')

    expect(wrapper.emitted()).toHaveProperty('submit')
    expect(wrapper.emitted('submit')[0][0]).toEqual({ name: 'Иван', email: 'ivan@example.com' })
  })
})

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


Тестирование Vuetify с Vitest сочетает быстрый рендеринг, поддержку слотов, событий и реактивных свойств, позволяя создавать стабильные, проверяемые интерфейсы. Основные методы — mount, trigger, setValue, мокирование глобальных функций и снапшоты — дают полный инструментарий для контроля визуального и функционального поведения компонентов.