логотип PurpleSchool
логотип PurpleSchool

Тестирование компонентов и приложений на Vue

Автор

Олег Марков

Введение

Тестирование — неотъемлемая часть современного JavaScript-разработки, и для проектов на Vue.js оно особенно важно. Вам нужно убедиться, что компоненты работают корректно, приложение надежно, а баги обнаруживаются вовремя. Сегодня мы разберёмся, как тестировать UI-компоненты и бизнес-логику во Vue-проектах. Я расскажу вам, какие существуют типы тестов, какие инструменты стоит выбрать, как писать простые и сложные тесты с примерами и пояснениями.

Из этой статьи вы узнаете:

  • Какие виды тестирования применяются к Vue-компонентам;
  • Как настроить среду для тестирования;
  • Какие инструменты и библиотеки используются;
  • Как писать юнит-тесты, интеграционные и end-to-end тесты для Vue-приложений;
  • Лучшие практики и полезные советы.

Классификация тестирования во Vue

Перед тем как писать тесты, давайте разберёмся, какие основные виды тестирования вы встретите.

Юнит-тесты

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

  • Например, вы хотите протестировать работу метода в компоненте или реакцию на изменение props.

Интеграционные тесты

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

  • Отображение родительского компонента вместе с дочерними;
  • Проверка правильной передачи данных между компонентами.

E2E (End-to-End) тесты

На этом уровне вы эмулируете поведение реального пользователя: клики, ввод текста, переходы по страницам. Обычно запускаются в браузере или эмуляторе.

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

Настройка среды тестирования Vue

Давайте настроим всё необходимое, чтобы ваши тесты работали гладко.

Установка необходимых библиотек

Vue Test Utils

Это официальная библиотека для тестирования компонентов Vue на уровне юнитов и интеграции. Она позволяет монтировать компонент, симулировать ввод, инициировать события.

npm install --save-dev @vue/test-utils

Jest

Самый популярный тестовый раннер и фреймворк для Vue, поддерживает быстрый запуск тестов, снапшот-тестирование, мокинг. Устанавливается так:

npm install --save-dev jest vue-jest babel-jest

Дополнительно: тестирование UI

Для E2E-тестов чаще всего используют Cypress или Playwright. Установка Cypress:

npm install --save-dev cypress

Базовая конфигурация Jest для Vue

После установки создайте файл jest.config.js:

module.exports = {
  moduleFileExtensions: ['js', 'json', 'vue'],
  transform: {
    '^.+\\.vue$': 'vue-jest',   // Преобразует .vue файлы для Jest
    '^.+\\.js$': 'babel-jest'
  },
  testMatch: ['**/__tests__/**/*.spec.[jt]s?(x)'], // Путь к тестам
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1'
  }
};

Юнит-тесты компонентов Vue

Давайте разберёмся, как тестировать отдельные компоненты на практике.

Структура юнит-теста

  1. Подготовка: импортируйте компонент и утилиты тестирования;
  2. Монтирование компонента;
  3. Имитация пользовательских действий (клик, ввод);
  4. Проверка изменений (в DOM, состоянии, событиях).

Пример юнит-теста кнопки

Давайте тестировать простой компонент кнопки:

<!-- ButtonCounter.vue -->
<template>
  <button @click="increment">{{ count }}</button>
</template>

<script>
export default {
  data() {
    return { count: 0 }
  },
  methods: {
    increment() {
      this.count++
    }
  }
}
</script>

А теперь сам тест:

// ButtonCounter.spec.js
import { mount } from '@vue/test-utils'
import ButtonCounter from '@/components/ButtonCounter.vue'

test('увеличивает счетчик при клике', async () => {
  const wrapper = mount(ButtonCounter)
  await wrapper.find('button').trigger('click')
  expect(wrapper.text()).toBe('1')
})
  • mount() монтирует компонент изолированно для тестирования.
  • trigger('click') симулирует клик.
  • expect(wrapper.text()).toBe('1') — проверяет, что после клика счетчик обновился.

Пример теста с props и событиями

Предположим у нас есть компонент:

<!-- HelloUser.vue -->
<template>
  <div>
    <span>Привет, {{ name }}!</span>
    <button @click="$emit('logout')">Выйти</button>
  </div>
</template>

<script>
export default {
  props: ['name']
}
</script>

Тестируем:

// HelloUser.spec.js
import { mount } from '@vue/test-utils'
import HelloUser from '@/components/HelloUser.vue'

test('отображает имя и триггерит событие выхода', async () => {
  const wrapper = mount(HelloUser, {
    props: { name: 'Анна' }
  })

  expect(wrapper.text()).toContain('Анна')
  await wrapper.find('button').trigger('click')
  expect(wrapper.emitted().logout).toBeTruthy() // Событие "logout" было вызвано
})

Интеграционные тесты Vue-компонентов

Здесь мы покрываем связки из нескольких компонентов или работу с хранилищем.

Пример тестирования родителя и детей

Допустим, есть такой родительский компонент:

<!-- TodoList.vue -->
<template>
  <ul>
    <TodoItem v-for="item in items" :key="item.id" :text="item.text" @remove="removeItem(item.id)" />
  </ul>
</template>

<script>
import TodoItem from './TodoItem.vue'

export default {
  components: { TodoItem },
  props: ['items'],
  methods: {
    removeItem(id) {
      this.$emit('item-removed', id)
    }
  }
}
</script>

И дочерний компонент:

<!-- TodoItem.vue -->
<template>
  <li>
    {{ text }}
    <button @click="$emit('remove')">Удалить</button>
  </li>
</template>

<script>
export default {
  props: ['text']
}
</script>

Тестируем, что удаление задача работает от события дочернего:

// TodoList.spec.js
import { mount } from '@vue/test-utils'
import TodoList from '@/components/TodoList.vue'

test('удаляет элемент списка при клике на дочерней кнопке', async () => {
  const items = [
    { id: 1, text: 'Посуду помыть' },
    { id: 2, text: 'Выбросить мусор' }
  ]
  const wrapper = mount(TodoList, { props: { items } })
  // Находим вторую кнопку "Удалить" и кликаем
  await wrapper.findAll('button')[1].trigger('click')
  // Проверяем, что emit был вызван с id=2
  expect(wrapper.emitted('item-removed')[0]).toEqual([2])
})

Тестирование работы со Vuex

Если в проекте используется глобальное хранилище, тестировать компоненты стоит не напрямую через стор, а с помощью моков.

Смотрите, как это реализовано:

import { shallowMount } from '@vue/test-utils'
import { createStore } from 'vuex'
import MyComponent from '@/components/MyComponent.vue'

const store = createStore({
  state() {
    return { count: 100 }
  },
  getters: {
    getCount: (state) => state.count
  }
})

test('отображает значение из Vuex', () => {
  const wrapper = shallowMount(MyComponent, {
    global: {
      plugins: [store] // Передаем store в компонент
    }
  })
  // Проверяем, что компонент вывел нужное значение
  expect(wrapper.text()).toContain('100')
})

Тестирование асинхронных данных

Часто компоненты работают с асинхронными запросами (fetch, axios). Используйте мок для HTTP-запросов.

Пример: мок axios и проверка загрузки данных

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

jest.mock('axios') // Мокаем весь модуль axios

test('выводит список пользователей после подгрузки', async () => {
  // Мокаем ответ сервера
  axios.get.mockResolvedValue({
    data: [{ id: 1, name: 'Олег' }, { id: 2, name: 'Аня' }]
  })

  const wrapper = mount(UsersList)
  // Ждем окончания всех промисов
  await wrapper.vm.$nextTick()

  // Проверяем рендер результата
  expect(wrapper.text()).toContain('Олег')
  expect(wrapper.text()).toContain('Аня')
})

Работа с снапшот-тестами Vue

Jest позволяет делать снапшоты — "замороженные" снимки вывода компонента.

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

test('вывод соответствует ожиданиям (snapshot)', () => {
  const wrapper = mount(ButtonCounter)
  expect(wrapper.html()).toMatchSnapshot()
})

Если через время ваш верстка изменится, тест упадет, подсказывая, что вывод компонента изменился — это помогает предотвращать неожиданные визуальные баги.


E2E-тесты для Vue — практический пример

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

Как устроены E2E-тесты с Cypress

  1. Запуск dev-сервера (npm run serve)
  2. Описываем сценарий пользователя:
// cypress/integration/login.spec.js
describe('Страница входа', () => {
  it('логинится с валидными данными', () => {
    cy.visit('/login') // Переходим по адресу
    cy.get('input[name=email]').type('user@mail.com') // Вводим данные
    cy.get('input[name=password]').type('123456')
    cy.get('button[type=submit]').click() // Кликаем "Войти"
    cy.url().should('include', '/dashboard') // Проверяем редирект
  })
})

Вместе с Cypress идут инструменты для мокинга API, проверки cookie, работы с localStorage и многое другое.


Лучшие практики для тестирования Vue

Собрал для вас ряд советов, которые облегчат тестирование ваших приложений:

  • Пишите короткие и атомарные тесты — они проще читаются и поддерживаются;
  • Используйте фабрики и хелперы, если тесты начинают дублироваться;
  • Мокаем внешние зависимости/данные;
  • Стремитесь к тому, чтобы тесты были "белыми ящиками" — тестируйте не то, как реализовано, а какое поведение ожидается на выходе;
  • Для E2E тестов старайтесь использовать "чистую" среду: отдельную базу, заполнение тестовыми данными;
  • Следите за покрытием кода тестами. Инструменты типа [coverage] в Jest покажут процент тестируемого кода;
  • Старайтесь не тестировать то, что уже протестировали браузеры или фреймворки (например, работу встроенных событий click).

Заключение

Тестирование компонентов и приложений на Vue не только предотвращает баги, но и делает ваше приложение надёжнее и проще в поддержке. С помощью Vue Test Utils и Jest вы можете легко покрыть юнит и интеграционные сценарии. Инструменты типа Cypress отлично справляются с имитацией пользовательских сценариев и помогают быть уверенным в качестве продукта. Не забывайте, что регулярное обновление тестов и стремление к "живому" покрытию — залог устойчивого развития вашего проекта.


Часто задаваемые технические вопросы

Как протестировать роутинг во Vue-компонентах?

Чтобы тестировать компоненты, использующие Vue Router, подключайте его в глобальные плагины:

import { mount } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({ history: createWebHistory(), routes: [] })

mount(MyComponent, {
  global: { plugins: [router] }
})

Теперь вы можете вызывать методы роутера (router.push('/about')) прямо в тестах.

Как мокать provide/inject во Vue 3?

В mount передавайте опцию global.provide:

mount(MyComponent, {
  global: { provide: { myKey: 'mockValue' } }
})

Компонент получит значение вместо настоящего провайдера.

Как тестировать слоты компонентов?

Передайте слот как строку или через функцию:

mount(MyComponent, {
  slots: { default: '<span>Текст в слоте</span>' }
})

Проверьте, что слот корректно отрисован.

Почему mount vs shallowMount?

mount делает полный рендер дочерних компонентов, shallowMount заменяет детей заглушками. Используйте shallowMount для изоляции и ускорения тестов, если детям доверяете.

Как добавить code coverage отчёт для Jest?

Запускайте тесты с флагом:

npx jest --coverage

В результате вы увидите отчёт о покрытии и сможете выявить незатестированные места.

Стрелочка влевоИспользование transition во VueРабота с teleport для управления DOM во VueСтрелочка вправо

Постройте личный план изучения Vue до уровня Middle — бесплатно!

Vue — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueРабота с teleport для управления DOM во VueПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиРуководство по nextTick для работы с DOMСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний

Лучшие курсы по теме

изображение курса

Vue 3 и Pinia

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.9
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий