Общие советы

Понимание что тестировать

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

Вместо этого, мы рекомендуем писать тесты, которые проверяют ваш публичный интерфейс взаимодействия с компонентом и относиться к его внутренностям как к чёрному ящику. Один тестовый пример должен проверять, что некоторые входные данные (взаимодействие пользователя или изменение входных параметров), предоставляемые компоненту будут приводить к ожидаемому результату (результату рендеринга или вызванным пользовательским событиям).

Например, для компонента Counter, который при каждом нажатии кнопки будет увеличивать отображаемый счётчик на 1, может тестироваться с помощью симуляции клика и проверке, что в отрендренном результате значение будет увеличено на 1. Тест не заботится о том, каким образом Counter увеличивает значение, он сосредоточен только на входных данных и результате.

Преимуществом этого подхода в том, что до тех пор пока интерфейс вашего компонента остаётся прежним, ваши тесты будут проходить независимо от того, как будет меняться внутренняя реализация компонента с течением времени.

Эта тема обсуждается более подробно в отличной презентации Matt O'Connell.

Поверхностный рендеринг

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

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

vue-test-utils позволяет вам монтировать компонент без рендеринга его дочерних компонентов (заменяя их заглушками) с помощью метода shallowMount:

import { shallowMount } from '@vue/test-utils'

const wrapper = shallowMount(Component)
wrapper.vm // примонтированный экземпляр Vue

Проверка вызванных событий

Каждая примонтированная обёртка автоматически записывает все события, вызванные на экземпляре Vue. Вы можете получить записанные события с помощью метода wrapper.emitted():

wrapper.vm.$emit('foo')
wrapper.vm.$emit('foo', 123)

/*
`wrapper.emitted()` возвращает указанный объект:
{
  foo: [[], [123]]
}
*/

Затем вы можете добавить проверки на основе этих данных:

// проверка, что событие было вызвано
expect(wrapper.emitted().foo).toBeTruthy()

// проверка, что событие вызывалось определённое число раз
expect(wrapper.emitted().foo.length).toBe(2)

// проверка, что с событием были переданы определённые данные
expect(wrapper.emitted().foo[1]).toEqual([123])

Вы также можете получить массив событий в порядке их вызова с помощью wrapper.emittedByOrder().

События дочерних компонентов

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

Тестируемый компонент

<template>
  <div>
    <child-component @custom="onCustom" />
    <p v-if="emitted">Emitted!</p>
  </div>
</template>

<script>
  import ChildComponent from './ChildComponent'

  export default {
    name: 'ParentComponent',
    components: { ChildComponent },
    data() {
      return {
        emitted: false
      }
    },
    methods: {
      onCustom() {
        this.emitted = true
      }
    }
  }
</script>

Тест

import { shallowMount } from '@vue/test-utils'
import ParentComponent from '@/components/ParentComponent'
import ChildComponent from '@/components/ChildComponent'

describe('ParentComponent', () => {
  it("displays 'Emitted!' when custom event is emitted", () => {
    const wrapper = shallowMount(ParentComponent)
    wrapper.find(ChildComponent).vm.$emit('custom')
    expect(wrapper.html()).toContain('Emitted!')
  })
})

Манипулирование состоянием компонента

Вы можете напрямую манипулировать состоянием компонента с помощью методов setData или setProps на обёртке:

wrapper.setData({ count: 10 })

wrapper.setProps({ foo: 'bar' })

Моки входных параметров

Вы можете передать входные параметры в компонент с использованием встроенной во Vue опции propsData:

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

mount(Component, {
  propsData: {
    aProp: 'some value'
  }
})

Вы также можете обновить входные параметры на уже примонтированном компоненте с помощью метода wrapper.setProps({}).

Полный список опции можно посмотреть в секции настроек монтирования документации.

Добавление глобальных плагинов и примесей

Некоторые из компонентов могут полагаться на функции, добавляемые глобальным плагином или примесью, к примеру vuex и vue-router.

Если вы пишете тесты для компонентов определённого приложения, вы можете настроить одни и те же глобальные плагины и примеси один раз перед началом ваших тестов. Но в некоторых случаях, например при тестировании набора общих компонентов, которые могут использоваться в разных приложениях, гораздо лучше протестировать ваши компоненты в более изолированной конфигурации, без загрязнения глобального конструктора Vue. Мы можем использовать метод createLocalVue для достижения этого:

import { createLocalVue } from '@vue/test-utils'

// создаём расширенный конструктор `Vue`
const localVue = createLocalVue()

// устанавливаем плагины как обычно
localVue.use(MyPlugin)

// передаём `localVue` в настройки монтирования
mount(Component, {
  localVue
})

Обратите внимание, что некоторые плагины, такие как Vue Router, добавляют свойства только для чтения к глобальному конструктору Vue. Это делает невозможным переустановку плагина на конструкторе localVue или добавление моков для этих свойств

Создание моков инъекций

Другая стратегия для инъекции входных параметров — просто создание их моков. Вы можете это сделать с помощью опции mocks:

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

const $route = {
  path: '/',
  hash: '',
  params: { id: '123' },
  query: { q: 'hello' }
}

mount(Component, {
  mocks: {
    // добавление мока объекта `$route` в экземпляр Vue
    // перед монтированием компонента
    $route
  }
})

Подмена компонентов

Вы можете переопределить компоненты, зарегистрированные глобально или локально, используя опцию stubs:

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

mount(Component, {
  // Компонент globally-registered-component
  // будет заменяться пустой заглушкой
  stubs: ['globally-registered-component']
})

Работа с маршрутизацией

Поскольку маршрутизация по определению имеет отношение к общей структуре приложения и включает в себя несколько компонентов, её лучше всего тестировать с помощью интеграционных или end-to-end тестов. Для отдельных компонентов, которые используют возможности vue-router, вы можете создать моки с использованием упомянутых выше методов.

Обнаружение стилей

Ваш тест может обнаруживать только встроенные стили при в jsdom.