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

Эта статья доступна в виде скринкаста на курсах Vue.js.

Часто идеально начинать каждый модульный тест с новой копии компонента. Кроме того, по мере того, как ваши приложения становятся больше и сложнее, скорее всего, у вас есть некоторые компоненты с множеством различных свойств и, возможно, установлен ряд сторонних библиотек, таких как Vuetify, VueRouter и Vuex. Это может привести к тому, что в ваших тестах будет много шаблонного кода, то есть кода, который не имеет прямого отношения к тесту.

Исходный код теста, описанного на этой странице, можно найти здесь.

Это компонент, который мы будем тестировать. Он показывает message опору, если она получена. Он показывает кнопку New Post, если пользователь аутентифицирован, и несколько сообщений. Оба объекта authenticated и posts взяты из магазина Vuex. Наконец, он отображает router-link компонент, показывающий ссылку на сообщение.

Мы хотим протестировать:

  • рендерится ли message при получении опоры?
  • правильно ли отображаются posts?
  • отображается ли кнопка «Новое сообщение», когда authenticated равно true, и скрыта, когда false?

В идеале тесты должны быть максимально краткими.

Один хороший шаг, который вы можете сделать, чтобы сделать приложения более тестируемыми, - это экспорт фабричных функций для Vuex и VueRouter. Часто вы увидите что-то вроде:

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

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

Теперь ваше основное приложение может выполнять import { store } from './store.js, а ваши тесты могут каждый раз получать новую копию хранилища, выполняя import { createStore } from './store.js', а затем создавая и создавая экземпляр с const store = createStore(). То же самое и с роутером. Это то, что я делаю в примере Posts.vue - код магазина находится здесь, а маршрутизатор - здесь.

Теперь мы знаем, как Posts.vue, а также магазин и маршрутизатор выглядят, мы можем понять, что делают тесты:

Это не полностью проверяет все условия; это минимальный пример, и его достаточно, чтобы начать работу. Обратите внимание на дублирование и повторение - давайте избавимся от этого.

Пользовательская функция createTestVue

Первые пять строк каждого теста одинаковы:

Давай исправим это. Чтобы не путать с функцией createLocalVue Vue Test Utils, я предпочитаю называть свою функцию createTestVue. Это выглядит примерно так:

Теперь мы заключили всю логику в одну функцию. Мы возвращаем store, router и localVue, поскольку нам нужно передать их функции mount.

Если мы реорганизуем первый тест с использованием createTestVue, он будет выглядеть так:

Немного короче. Давайте проведем рефакторинг второго теста, который использует хранилище Vuex.

Определение метода createWrapper

Хотя приведенный выше код определенно является улучшением, сравнивая этот и предыдущий тест, мы можем заметить, что примерно половина кода все еще дублируется. Давайте создадим новый метод createWrapper, чтобы решить эту проблему.

Теперь мы можем просто вызвать createWrapper и получить новую копию компонента, готовую для тестирования. Наши тесты теперь очень краткие.

Последнее улучшение, которое мы можем сделать, - это то, как мы заполняем магазин Vuex. В реальном приложении ваше хранилище, вероятно, будет сложным, и необходимость commit и dispatch множества различных мутаций и действий для приведения вашего компонента в состояние, которое вы хотите протестировать, не идеальна. Мы можем внести небольшое изменение в нашу createStore функцию, которая упростит установку начального состояния:

Теперь мы можем задать желаемое начальное состояние функции createStore. Мы можем провести быстрый рефакторинг, объединив createTestVue и createWrapper:

Теперь наш тест можно записать следующим образом:

Это большое улучшение! Мы перешли от теста, где примерно половина кода была шаблонной и фактически не связана с утверждением, к двум строкам; один для подготовки компонента к тестированию и один для утверждения.

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

Есть и другие возможные улучшения:

  • обновите функцию createStore, чтобы разрешить установку начального состояния для модулей с пространством имен Vuex
  • улучшить createRouter, чтобы задать конкретный маршрут
  • разрешить пользователю передавать shallow или mount аргумент createWrapper

В этом руководстве обсуждаются:

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

Исходный код теста, описанного на этой странице, можно найти здесь. Он также доступен в виде скринкаста на курсах Vue.js.