Тестирование
Зачем тестировать?
Автоматические тесты помогут вам и вашей команде быстро и уверенно создавать сложные приложения Vue, предотвращая регрессии и побуждая вас разбивать приложение на тестируемые функции, модули, классы и компоненты. Как и любое другое приложение, ваше новое приложение Vue может сломаться по многим причинам, и очень важно, чтобы вы могли отловить эти проблемы и исправить их до выпуска.
В этом руководстве мы рассмотрим основную терминологию и дадим рекомендации по выбору инструментов для вашего приложения Vue 3.
Есть один специфический для Vue раздел, посвященный композитным элементам. Подробнее см. в разделе Тестирование композаблов ниже.
Когда тестировать
Начните тестирование как можно раньше! Мы рекомендуем начать писать тесты как можно раньше. Чем дольше вы ждете, чтобы добавить тесты в приложение, тем больше зависимостей будет у вашего приложения, и тем сложнее будет его запустить.
Виды тестирования
При разработке стратегии тестирования вашего приложения Vue вам следует использовать следующие типы тестирования:
- Модульное: проверяет, что входные данные для данной функции, класса или компонуемого объекта дают ожидаемый результат или побочные эффекты.
- Компонентное: проверяет, что ваш компонент монтируется, отображается, с ним можно взаимодействовать и ведет себя должным образом. Эти тесты импортируют больше кода, чем модульные тесты, они более сложны и требуют больше времени для выполнения.
- Сквозное: проверяет функции, охватывающие несколько страниц, и отправляет реальные сетевые запросы к вашему рабочему приложению Vue. Эти тесты часто включают в себя проверку базы данных или другого бэкэнда.
Каждый тип тестирования играет свою роль в стратегии тестирования вашего приложения, и каждый из них защитит вас от различных типов проблем.
Обзор
Мы кратко обсудим, что представляет собой каждый из них, как их можно реализовать для приложений Vue, а также дадим некоторые общие рекомендации.
Модульное тестирование
Модульные тесты пишутся для проверки того, что небольшие изолированные блоки кода работают должным образом. Модульный тест обычно охватывает одну функцию, класс, составной элемент или модуль. Модульные тесты фокусируются на логической корректности и касаются лишь небольшой части общей функциональности приложения. Они могут имитировать большие части среды вашего приложения (например, исходное состояние, сложные классы, сторонние модули и сетевые запросы).
В общем, модульные тесты выявляют проблемы с бизнес-логикой функции и её логической корректностью.
Возьмем, к примеру, эту функцию increment
:
js
// helpers.js
export function increment(current, max = 10) {
if (current < max) {
return current + 1
}
return current
}
Поскольку он очень автономен, будет легко вызвать функцию increment
и убедиться, что она возвращает то, что должно, поэтому мы напишем модульный тест.
Если какое-либо из этих утверждений не сработает, ясно, что проблема кроется в функции increment
.
js
// helpers.spec.js
import { increment } from './helpers'
describe('increment', () => {
test('увеличивает текущее число на 1', () => {
expect(increment(0, 10)).toBe(1)
})
test('не увеличивает текущее число сверх максимального', () => {
expect(increment(10, 10)).toBe(10)
})
test('имеет максимальное значение по умолчанию 10', () => {
expect(increment(10)).toBe(10)
})
})
Как упоминалось ранее, модульное тестирование обычно применяется к автономной бизнес-логике, компонентам, классам, модулям или функциям, которые не связаны с отрисовкой пользовательского интерфейса, сетевыми запросами или другими проблемами окружающей среды.
Обычно это простые модули JavaScript/TypeScript, не связанные с Vue. В целом написание модульных тестов для бизнес-логики в приложениях Vue существенно не отличается от приложений, использующих другие фреймворки.
Есть два случая, когда вы СДЕЛАЕТЕ модульное тестирование функций Vue:
- Композаблы
- Компоненты
Композаблы
Одной из категорий функций, специфичных для приложений Vue, является Композаблы, которая может потребовать специальной обработки во время тестов. Дополнительные сведения см. в разделе Тестирование композаблов ниже.
Компоненты модульного тестирования
Компонент можно протестировать двумя способами:
Белый ящик: модульное тестирование
Тесты, являющиеся «тестами белого ящика», учитывают детали реализации и зависимости компонента. Они сосредоточены на изоляции тестируемого компонента. Эти тесты обычно включают в себя имитацию некоторых, если не всех, дочерних элементов вашего компонента, а также настройку состояния и зависимостей плагина (например, Pinia).
Чёрный ящик: тестирование компонентов
Тесты, являющиеся «тестами чёрного ящика», не знают деталей реализации компонента. Эти тесты как можно меньше имитируют интеграцию вашего компонента и всей системы. Обычно они отображают все дочерние компоненты и считаются скорее «интеграционным тестом». См. Рекомендации по тестированию компонентов ниже.
Рекомендация
Поскольку официальная установка, созданная с помощью
create-vue
, основана на Vite, мы рекомендуем использовать среду модульного тестирования, которая может использовать ту же конфигурацию и преобразовывать конвейер непосредственно из Vite. Vitest — это среда модульного тестирования, разработанная специально для этой цели, созданная и поддерживаемая членами команды Vue/Vite. Он интегрируется с проектами на базе Vite с минимальными усилиями и работает очень быстро.
Другие параметры
- Jest is a popular unit testing framework. However, we only recommend Jest if you have an existing Jest test suite that needs to be migrated over to a Vite-based project, as Vitest offers a more seamless integration and better performance.
Тестирование компонентов
В приложениях Vue компоненты являются основными строительными блоками пользовательского интерфейса. Таким образом, компоненты являются естественной единицей изоляции, когда дело доходит до проверки поведения вашего приложения. С точки зрения детализации тестирование компонентов находится где-то выше модульного тестирования и может рассматриваться как форма интеграционного тестирования. Большая часть вашего приложения Vue должна быть подвергнута тестированию компонентов, и мы рекомендуем, чтобы у каждого компонента Vue был собственный файл спецификаций.
Тесты компонентов должны выявлять проблемы, связанные с параметрами вашего компонента, событиями, слотами, которые он предоставляет, стилями, классами, перехватчиками жизненного цикла и т. д.
Тесты компонентов не должны имитировать дочерние компоненты, а вместо этого проверять взаимодействие между вашим компонентом и его дочерними элементами, взаимодействуя с компонентами так, как это сделал бы пользователь. Например, тест компонента должен щелкнуть элемент, как это сделал бы пользователь, вместо того, чтобы программно взаимодействовать с компонентом.
Тесты компонентов должны быть сосредоточены на общедоступных интерфейсах компонента, а не на деталях внутренней реализации. Для большинства компонентов общедоступный интерфейс ограничен: создаваемыми событиями, параметрами и слотами. При тестировании не забывайте проверять, что делает компонент, а не то, как он это делает.
ДЕЛАЕМ
Для визуальной логики: подтвердите правильный вывод рендеринга на основе введённых параметров и слотов.
Для поведенческой логики: подтверждайте правильные обновления рендеринга или создаваемые события в ответ на события пользовательского ввода.
В приведенном ниже примере мы демонстрируем компонент Stepper, который имеет элемент DOM с надписью «increment», и по которому можно щелкнуть. Мы передаем свойство под названием
max
, которое предотвращает увеличение шагового значения выше2
, поэтому, если мы нажмем кнопку 3 раза, пользовательский интерфейс все равно должен сказать2
.Мы ничего не знаем о реализации Stepper, только то, что «вход» — это свойство max, а «выход» — это состояние DOM, каким его увидит пользователь.
Vue Test Utils
Cypress
Testing Library
js
const valueSelector = '[data-testid=stepper-value]'
const buttonSelector = '[data-testid=increment]'
const wrapper = mount(Stepper, {
props: {
max: 1
}
})
expect(wrapper.find(valueSelector).text()).toContain('0')
await wrapper.find(buttonSelector).trigger('click')
expect(wrapper.find(valueSelector).text()).toContain('1')
НЕ ДЕЛАЕМ
Не утверждайте частное состояние экземпляра компонента и не тестируйте частные методы компонента. Детали реализации тестирования делают тесты хрупкими, поскольку они с большей вероятностью сломаются и потребуют обновлений при изменении реализации.
Конечная задача компонента — отрисовка правильного вывода DOM, поэтому тесты, ориентированные на вывод DOM, обеспечивают тот же уровень гарантии правильности (если не больше), будучи более надёжными и устойчивыми к изменениям.
Не полагайтесь исключительно на тесты моментальных снимков. Утверждение строк HTML не описывает правильность. Пишите тесты намеренно.
Если метод необходимо тщательно протестировать, рассмотрите возможность выделения его в отдельную служебную функцию и напишите для него специальный модульный тест. Если его невозможно извлечь чисто, его можно протестировать как часть охватывающего его компонента, интеграции или сквозного теста.
Рекомендация
Vitest для компонентов или композаблов, которые обрабатывают логику и поведение, отделённые от пользовательского интерфейса (например, функция
useFavicon
во VueUse). Компоненты и DOM можно протестировать с помощью@vue/test-utils
.Тестирование компонентов Cypress для компонентов, ожидаемое поведение которых зависит от правильного рендеринга стилей или запуска собственных событий DOM. Его можно использовать с библиотекой тестирования через @testing-library/cypress.
Основные различия между Vitest и браузерными раннерами — это скорость и контекст выполнения. Короче говоря, средства запуска на основе браузера, такие как Cypress, могут выявлять проблемы, которые не могут обнаружить средства запуска на основе узлов, такие как Vitest (например, проблемы со стилем, настоящие собственные события DOM, файлы cookie, локальное хранилище и сбои сети), но средства запуска на основе браузера на порядок медленнее, чем Vitest, потому что они открывают браузер, компилируют ваши таблицы стилей и многое другое. Cypress — это браузерный раннер, поддерживающий тестирование компонентов. Пожалуйста, прочтите страницу сравнения Vitest для получения последней информации о сравнении Vitest и Cypress.
Монтирование библиотек
Тестирование компонентов часто включает в себя изолированное монтирование тестируемого компонента, запуск имитируемых событий пользовательского ввода и утверждение отображаемых выходных данных DOM. Существуют специальные библиотеки утилит, которые упрощают эти задачи.
@vue/test-utils
— официальная библиотека низкоуровневого тестирования компонентов, написанная для предоставления пользователям доступа к API-интерфейсам Vue. Это также библиотека нижнего уровня@testing-library/vue
, построенная поверх нее.
— @testing-library/vue
— это библиотека тестирования Vue, ориентированная на тестирование компонентов, не полагаясь на детали реализации. Его руководящий принцип заключается в том, что чем больше тесты напоминают способ использования программного обеспечения, тем больше уверенности они могут обеспечить.
Мы рекомендуем использовать @vue/test-utils
для тестирования компонентов в приложениях. @testing-library/vue
имеет проблемы с тестированием асинхронного компонента с помощью Suspense, поэтому его следует использовать с осторожностью.
Другие параметры
Nightwatch — средство запуска тестов E2E с поддержкой тестирования компонентов Vue. (Пример проекта)
WebdriverIO для кросс-браузерного тестирования компонентов, основанного на собственном взаимодействии с пользователем на основе стандартизированной автоматизации. Его также можно использовать с библиотекой тестирования.
E2E-тестирование
Хотя модульные тесты дают разработчикам некоторую степень уверенности, модульные и компонентные тесты ограничены в своих возможностях обеспечить целостное покрытие приложения при его развертывании в рабочей среде. В результате сквозные тесты (E2E) охватывают, пожалуй, самый важный аспект приложения: что происходит, когда пользователи действительно используют ваши приложения.
Сквозные тесты фокусируются на поведении многостраничных приложений, которые отправляют сетевые запросы к вашему рабочему приложению Vue. Они часто включают в себя работу с базой данных или другим серверным компонентом и могут даже выполняться в живой промежуточной среде.
Сквозные тесты часто выявляют проблемы с вашим маршрутизатором, библиотекой управления состоянием, компонентами верхнего уровня (например, приложением или макетом), общедоступными активами или любой обработкой запросов. Как указано выше, они выявляют критические проблемы, которые невозможно выявить с помощью модульных или компонентных тестов.
Сквозные тесты не импортируют какой-либо код вашего приложения Vue, а вместо этого полностью полагаются на тестирование вашего приложения путём навигации по целым страницам в реальном браузере.
Сквозные тесты проверяют многие уровни вашего приложения. Они могут быть нацелены либо на ваше локально созданное приложение, либо даже на живую промежуточную среду. Тестирование в вашей промежуточной среде включает не только внешний код и статический сервер, но и все связанные с ним серверные службы и инфраструктуру.
Чем больше ваши тесты напоминают то, как используется ваше программное обеспечение, тем больше уверенности они могут вам дать. - Кент К. Доддс – автор библиотеки тестирования
Тестирование E2E, проверяющее, как действия пользователя влияют на ваше приложение, часто является ключом к большей уверенности в том, правильно ли работает приложение.
Выбор решения для тестирования E2E
В то время как сквозное (E2E) тестирование в сети приобрело негативную репутацию из-за ненадежных (ненадежных) тестов и замедления процессов разработки, современные инструменты E2E добились успехов в создании более надежных, интерактивных и полезных тестов. При выборе среды тестирования E2E в следующих разделах представлены некоторые рекомендации о том, что следует учитывать при выборе среды тестирования для вашего приложения.
Кроссбраузерное тестирование
Одним из основных преимуществ сквозного (E2E) тестирования является возможность тестировать ваше приложение в нескольких браузерах. Хотя может показаться желательным обеспечить 100% кроссбраузерность, важно отметить, что кроссбраузерное тестирование снижает отдачу от ресурсов команды из-за дополнительного времени и мощности компьютера, необходимых для его последовательного выполнения. В результате важно помнить об этом компромиссе при выборе объема кроссбраузерного тестирования, необходимого вашему приложению.
Более быстрые циклы обратной связи
Одна из основных проблем комплексного (E2E) тестирования и разработки заключается в том, что запуск всего пакета занимает много времени. Обычно это делается только в конвейерах непрерывной интеграции и развертывания (CI/CD). Современные среды тестирования E2E помогли решить эту проблему, добавив такие функции, как распараллеливание, которое позволяет конвейерам CI/CD часто работать намного быстрее, чем раньше. Кроме того, при локальной разработке возможность выборочного запуска одного теста для страницы, над которой вы работаете, а также обеспечение «горячей» перезагрузки тестов может помочь улучшить рабочий процесс и производительность разработчика.
Первоклассный опыт отладки
В то время как разработчики традиционно полагались на сканирование журналов в окне терминала, чтобы определить, что пошло не так в тесте, современные среды сквозного тестирования (E2E) позволяют разработчикам использовать инструменты, с которыми они уже знакомы, например инструменты разработчика браузера.
Видимость в режиме headless
Когда сквозные (E2E) тесты запускаются в конвейерах непрерывной интеграции/развертывания, они часто запускаются в автономных браузерах (т. е. видимый браузер не открывается для просмотра пользователем). Важнейшей особенностью современных сред тестирования E2E является возможность просматривать снимки и/или видео приложения во время тестирования, что дает некоторое представление о том, почему происходят ошибки. Исторически сложилось так, что поддерживать такую интеграцию было утомительно.
Рекомендация
Playwright — это отличное решение для E2E-тестирования, которое поддерживает Chromium, WebKit и Firefox. Тестируйте на Windows, Linux и macOS, локально или в CI, с графическим интерфейсом или без него, с нативной эмуляцией Google Chrome для Android и Mobile Safari. Он имеет информативный пользовательский интерфейс, отличную отлаживаемость, встроенные утверждения, распараллеливание, трассировку и предназначен для устранения нестабильных тестов. Поддержка Component Testing доступна, но отмечена как экспериментальная. Playwright имеет открытый исходный код и поддерживается компанией Microsoft.
Cypress имеет информативный графический интерфейс, отличную отлаживаемость, встроенные утверждения, заглушки, устойчивость к взлому и моментальные снимки. Как уже упоминалось выше, он обеспечивает стабильную поддержку Component Testing. Cypress поддерживает браузеры на базе Chromium, Firefox и Electron. Поддержка WebKit доступна, но отмечена как экспериментальная. Cypress находится под лицензией MIT, но некоторые функции, такие как распараллеливание, требуют подписки на Cypress Cloud.
Другие параметры
Nightwatch — это решение для E2E-тестирования на основе Selenium WebDriver. Это обеспечивает самую широкую поддержку браузеров, включая нативное мобильное тестирование. Решения на основе Selenium будут медленнее, чем Playwright или Cypress.
WebdriverIO — это платформа автоматизации тестирования для веб- и мобильных устройств, основанная на протоколе WebDriver.
Рецепты
Добавление Vitest в проект
В проекте Vue на базе Vite запустите:
sh
> npm install -D vitest happy-dom @testing-library/vue
Затем обновите конфигурацию Vite, добавив блок опций test
:
js
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
// ...
test: {
// включаем jest-подобные глобальные тестовые API
globals: true,
// Имитация DOM с помощью happy-dom
// (требует установки happy-dom в качестве зависимости)
environment: 'happy-dom'
}
})
Совет
Если вы используете TypeScript, добавьте vitest/globals
в поле types
в вашем tsconfig.json
.
json
// tsconfig.json
{
"compilerOptions": {
"types": ["vitest/globals"]
}
}
Затем создайте в своем проекте файл, заканчивающийся *.test.js
. Можно разместить все тестовые файлы в тестовом каталоге в корне проекта или в тестовых каталогах рядом с исходными файлами. Vitest будет автоматически искать их, используя соглашение об именовании.
js
// MyComponent.test.js
import { render } from '@testing-library/vue'
import MyComponent from './MyComponent.vue'
test('it should work', () => {
const { getByText } = render(MyComponent, {
props: {
/* ... */
}
})
// подтверждаем вывод
getByText('...')
})
Наконец, обновите package.json
, чтобы добавить тестовый сценарий, и запустите его:
json
{
// ...
"scripts": {
"test": "vitest"
}
}
sh
> npm test
Тестирование композаблов
Этот раздел предполагает, что вы прочитали раздел Композаблы.
Когда речь заходит о тестировании композаблов, их можно разделить на две категории: составные компоненты, которые не зависят от экземпляра хост-компонента, и составные компоненты, которые зависят.
Композабл зависит от экземпляра хост-компонента, если он использует следующие API:
- Хуки жизненного цикла
- Provide / Inject
Если композит использует только Reactivity API, то его можно протестировать, непосредственно вызвав его и утвердив возвращаемое состояние/методы:
js
// counter.js
import { ref } from 'vue'
export function useCounter() {
const count = ref(0)
const increment = () => count.value++
return {
count,
increment
}
}
js
// counter.test.js
import { useCounter } from './counter.js'
test('useCounter', () => {
const { count, increment } = useCounter()
expect(count.value).toBe(0)
increment()
expect(count.value).toBe(1)
})
Композабл, который полагается на хуки жизненного цикла или Provide / Inject, должен быть обёрнут в базовый компонент для тестирования. Мы можем создать помощника, как показано ниже:
js
// test-utils.js
import { createApp } from 'vue'
export function withSetup(composable) {
let result
const app = createApp({
setup() {
result = composable()
// Подавление предупреждения об отсутствии шаблона
return () => {}
}
})
app.mount(document.createElement('div'))
// верните результат и экземпляр приложения
// для тестирования provide/unmount
return [result, app]
}
js
import { withSetup } from './test-utils'
import { useFoo } from './foo'
test('useFoo', () => {
const [result, app] = withSetup(() => useFoo(123))
// обеспечиваем тестирование инъекций
app.provide(...)
// выполняем утверждения
expect(result.foo.value).toBe(1)
// при необходимости включаем хук onUnmounted
app.unmount()
})
Для более сложных композаблов также может быть проще протестировать их, написав тесты для компонента-обёртки, используя методы Тестирования компонентов.