Статьи

Когда ‘Unstub’ ‘Компонент в модульном тесте Vue.js

Чтобы протестировать компонент изолированно, вы можете заменить его дочерние компоненты, заглушив их. Vue Test Utils может автоматически сделать это для вас с помощью функции под названием shallowMount.

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

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

Примечание: эта статья была первоначально размещена здесь в блоге разработчиков Vue.js 2019/09/30.

Тестирование в изоляции

Ключевой идеей модульного тестирования является тестирование «модуля» приложения изолированно. В интерфейсных приложениях на основе компонентов мы рассматриваем «модуль» как компонент.

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

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

Создание заглушки для компонента обычно означает его замену простым «подставным» компонентом без состояния, логики и минимального шаблона.

Например, вы можете заменить это:

export default {
  name: "MyComponent",
  template: "..."
  props: { ... },
  methods: { ... },
  computed: { ... }
  ...
};

с этим:

export default {
  name: "MyComponentStub"
  template: "<div></div>"
};

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

Связанные компоненты

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

Например, скажем, мы создаем кнопку с классной анимацией и хотим повторно использовать ее в приложении, поэтому мы решили создать пользовательский компонент с именем animated-button.

Теперь у нас есть my-formкомпонент, который использует этот компонент кнопки. Он реализован так, что my-formсвязан с ним animated-button, поскольку последний генерирует событие «click», которое используется для запуска submitметода в первом.

MyForm.vue

<template>
  <input name="email" v-model="email" />
  <animated-button title="Submit" @click="submit" />
  <!--more markup and children components here-->
</template>
<script>
import AnimatedButton from "@/component/AnimatedButton";
...
export default {
  data: () => ({
    email: null
  }),
  methods: {
    submit () {
      this.$store.commit("FORM_SUBMIT", email);
    }
  }
  components: {
    AnimatedButton,
    AnotherChildComponent,
    SomeOtherChildComponent
    ...
  }
}
</script>

Модульное тестирование my-form

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

В my-formкомпоненте мы должны выполнить модульный тест, в котором вводом является нажатие кнопки, а выводом — фиксация Vuex.

Мы назовем этот тест «должен фиксировать FORM_SUBMIT при нажатии кнопки». Мы создадим его, сначала смонтировав MyForm, чтобы изолировать его от влияния любых дочерних компонентов, как было предписано ранее.

MyForm.spec.js

import { shallowMount } from "@vue/test-utils";
import MyForm from "@/components/MyForm";

describe("MyForm.vue", () => {
  it("should commit FORM_SUBMIT when button clicked", () => {
    const wrapper = shallowMount(MyForm);

  });
});

Далее мы будем использовать findметод API- оболочки, чтобы найти компонент кнопки. Мы передаем CSS-селектор "animated-button"в качестве стратегии локатора. Затем мы можем связать triggerметод и передать «click» в качестве аргумента. Вот как мы генерируем входные данные теста.

Затем мы можем утверждать, что был сделан коммит Vuex (возможно, с использованием шпиона, но это не относится к этой статье, поэтому я не буду его подробно описывать).

MyForm.spec.js

it("should commit FORM_SUBMIT when button clicked", () => {
  const wrapper = shallowMount(MyForm);
  wrapper.find("animated-button").trigger("click");
  // assert that $store.commit was called
});

Если мы попытаемся запустить это, мы получим эту ошибку от Vue Test Utils:

find не возвращает анимированную кнопку, не может вызвать trigger () для пустого Wrapper

Селектор CSS неправильный? Нет, проблема в том, что мы смонтировали компонент, поэтому все дети были зарезаны. Процесс автоматической заглушки изменяет имя AnimatedButton на «анимированная заглушка кнопки» в шаблоне.

Но изменение селектора с «анимированной кнопки» на «анимированную кнопку-заглушку» не является решением. Автоматические заглушки не имеют внутренней логики, поэтому событие нажатия, которое мы запускаем, не прослушивается в любом случае.

Выборочная расстегивание

Мы по-прежнему хотим быть мелкими my-form, так как хотим обеспечить их изоляцию от влияния своих детей. Но animated-buttonэто исключение, так как его функциональность требуется для теста.

Vue Test Utils позволяет нам указывать заглушку для конкретного компонента, а не использовать автоматическую заглушку при мелком монтаже. Таким образом, хитрость заключается в том, чтобы «удалить» animated-button, используя исходное определение компонента в качестве заглушки, чтобы он сохранил все свои функциональные возможности!

Чтобы сделать это, давайте импортируем компонент AnimatedButton вверху файла. Теперь давайте перейдем к нашему тесту, создадим const stubsи назначим ему объект. Мы можем указать AnimatedButtonв качестве свойства объекта сокращение.

Теперь мы передадим это stubsкак часть нашей мелкой конфигурации монтирования. Мы также заменим селектор CSS на определение компонента, так как это предпочтительный способ использования findметода.

MyForm.spec.js

import { shallowMount } from "@vue/test-utils";
import MyForm from "@/components/MyForm";
import AnimatedButton from "@/component/AnimatedButton"

describe("MyForm.vue", () => {
  it("should commit FORM_SUBMIT when button clicked", () => {
    const stubs = {
      AnimatedButton
    };
    const wrapper = shallowMount(MyForm, { stubs });
    wrapper.find(AnimatedButton).trigger("click");
    ...
  });
});

Делая это таким образом, вы получите зеленую галочку.

Заворачивать

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

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


Станьте старшим разработчиком Vue в 2020 году.


Узнайте и узнайте, что знают профессионалы о создании, тестировании и развертывании полнофункциональных приложений Vue в нашем последнем курсе.


Выучить больше