Стабирование директив в компонентах

В тестировании Vue-компонентов директивы представляют особую сложность. Пользовательские директивы часто работают напрямую с DOM, используют сторонние библиотеки, подписываются на события, управляют фокусом, анимациями или размерами элементов. В модульных тестах подобное поведение избыточно и мешает изоляции компонента. Стабирование директив позволяет:

  • исключить побочные эффекты;
  • ускорить выполнение тестов;
  • упростить проверку логики компонента;
  • избежать необходимости подключать реальные реализации директив.

Vue Test Utils предоставляет встроенные механизмы для подмены директив упрощёнными версиями или заглушками.


Как Vue Test Utils обрабатывает директивы

При монтировании компонента Vue Test Utils использует обычный механизм Vue. Это означает:

  • глобальные директивы учитываются;
  • локальные директивы из directives компонента подключаются автоматически;
  • хуки директив (beforeMount, mounted, updated, unmounted) реально выполняются.

Без стабирования директива будет вести себя так же, как в браузере, что нежелательно для unit-тестов.


Базовое стабирование директив через global.directives

Основной способ стабирования — передача заглушек в параметре global.directives при mount или shallowMount.

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

const wrapper = mount(MyComponent, {
  global: {
    directives: {
      focus: {
        mounted() {}
      }
    }
  }
})

В данном примере директива v-focus заменяется пустой реализацией. Все её хуки могут быть описаны явно или опущены.

Ключевые особенности:

  • имя директивы указывается без префикса v-;
  • можно стабировать как пользовательские, так и сторонние директивы;
  • стаб заменяет реальную директиву полностью.

Минимальные заглушки директив

Часто нет необходимости объявлять хуки вовсе. Vue Test Utils допускает использование пустого объекта:

directives: {
  tooltip: {}
}

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

Подобный подход подходит для директив, которые:

  • не влияют на рендеринг;
  • не участвуют в логике компонента;
  • используются только для UI-эффектов.

Отслеживание вызовов хуков директив

Иногда требуется проверить, что директива применяется к элементу. Для этого стаб может содержать шпионов.

const mountedSpy = vi.fn()

mount(MyComponent, {
  global: {
    directives: {
      intersect: {
        mounted: mountedSpy
      }
    }
  }
})

Это позволяет:

  • убедиться, что директива была инициализирована;
  • проверить количество вызовов;
  • проанализировать аргументы хука (el, binding, vnode).

Использование аргументов и модификаторов в стабах

Хуки директив получают объект binding, содержащий:

  • value
  • arg
  • modifiers
  • instance

Стаб может анализировать эти данные:

directives: {
  permission: {
    mounted(el, binding) {
      if (binding.value !== 'admin') {
        el.remove()
      }
    }
  }
}

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


Стабирование директив в shallowMount

shallowMount не стабирует директивы автоматически. Они продолжают выполняться, если не подменены явно.

shallowMount(MyComponent, {
  global: {
    directives: {
      resize: {}
    }
  }
})

Важно учитывать:

  • shallowMount стабирует дочерние компоненты, но не директивы;
  • директивы всегда требуют явного стабирования при необходимости.

Глобальные директивы и конфигурация тестовой среды

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

config.global.directives = {
  clickOutside: {}
}

Преимущества:

  • уменьшение дублирования кода;
  • единое поведение во всех тестах;
  • простота поддержки.

Недостаток — потеря гибкости, если в отдельных тестах требуется особое поведение директивы.


Частичное стабирование реальной директивы

Иногда требуется сохранить часть логики директивы, но отключить побочные эффекты.

import realDirective from '@/directives/scroll'

directives: {
  scroll: {
    mounted(el, binding) {
      // пропущена подписка на window
      realDirective.mounted(el, binding)
    }
  }
}

Подобный подход полезен, когда:

  • директива содержит сложную бизнес-логику;
  • требуется протестировать взаимодействие с компонентом;
  • внешние зависимости директивы мешают тестам.

Работа с директивами, изменяющими DOM

Директивы, которые:

  • добавляют/удаляют элементы;
  • меняют стили;
  • управляют атрибутами,

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

directives: {
  autoHeight: {
    mounted(el) {
      el.style.height = '100px'
    }
  }
}

Это позволяет сохранить предсказуемое состояние DOM.


Распространённые ошибки при стабировании директив

Неправильное имя директивы

directives: {
  vFocus: {} // ошибка
}

Используется имя без v- и без camelCase-преобразований шаблона.

Ожидание автоматического стабирования

Vue Test Utils не стабирует директивы автоматически ни при каких режимах монтирования.

Смешивание стабов и реальных глобальных директив

Если директива зарегистрирована глобально в приложении, но не стабирована в тесте, будет использована реальная версия.


Когда директиву не следует стабировать

Стабирование не всегда оправдано. Реальную директиву имеет смысл оставить, если:

  • она участвует в вычислении состояния компонента;
  • её логика критична для бизнес-правил;
  • тест направлен именно на проверку взаимодействия с директивой.

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


Итоговая роль стабирования директив

Стабирование директив в Vue Test Utils — инструмент управления сложностью тестов. Оно позволяет строго изолировать компонент, контролировать побочные эффекты и сфокусироваться на проверке реактивности, событий и вычисляемых состояний. Грамотное использование стабов делает тесты стабильными, быстрыми и легко поддерживаемыми.