Изоляция плагинов

Плагины во Vue расширяют глобальное поведение приложения: добавляют методы в app.config.globalProperties, регистрируют директивы, компоненты, внедряют зависимости через provide/inject, изменяют роутинг или состояние. В тестовой среде такое поведение становится источником неявных зависимостей и побочных эффектов. Изоляция плагинов позволяет:

  • тестировать компонент независимо от инфраструктуры приложения;
  • контролировать входные данные и состояние окружения;
  • ускорять тесты за счёт исключения тяжёлых зависимостей;
  • предотвращать утечки состояния между тестами.

Vue Test Utils предоставляет инструменты для точечного подключения, подмены и отключения плагинов.


Как плагины подключаются в Vue Test Utils

В Vue 3 плагины подключаются через опцию global.plugins при монтировании компонента:

import { mount } from '@vue/test-utils'
import MyPlugin from '@/plugins/my-plugin'

mount(MyComponent, {
  global: {
    plugins: [MyPlugin]
  }
})

Такой плагин будет установлен в тестовое приложение (app), создаваемое Vue Test Utils. Это приложение полностью изолировано от других тестов, но не изолировано внутри одного теста, если плагин хранит состояние на уровне модуля.


Проблема глобального состояния плагинов

Типичная ошибка — плагины с синглтон-состоянием:

// plugin.js
let counter = 0

export default {
  install(app) {
    app.config.globalProperties.$inc = () => ++counter
  }
}

При последовательном запуске тестов значение counter будет накапливаться, даже если каждый тест монтирует компонент заново. Vue Test Utils не сбрасывает состояние модулей JavaScript.

Решения:

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

Использование фабрик плагинов

Правильный шаблон — плагин как функция, возвращающая объект установки:

export function createCounterPlugin() {
  let counter = 0

  return {
    install(app) {
      app.config.globalProperties.$inc = () => ++counter
    }
  }
}

В тесте:

import { createCounterPlugin } from '@/plugins/counter'

mount(MyComponent, {
  global: {
    plugins: [createCounterPlugin()]
  }
})

Каждое монтирование получает полностью изолированное состояние.


Изоляция через подмену глобальных свойств

Если компонент использует $router, $t, $api или другие глобальные свойства, нет необходимости подключать полноценный плагин. Vue Test Utils позволяет напрямую задавать globalProperties:

mount(MyComponent, {
  global: {
    config: {
      globalProperties: {
        $t: (key) => key
      }
    }
  }
})

Такой подход:

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

Подмена плагинов моками

Когда плагин сложный (Vue Router, Pinia, i18n), используется частичная или полная подмена.

Пример: изоляция Vue Router

Вместо реального роутера:

const mockRouter = {
  push: vi.fn(),
  currentRoute: {
    value: { name: 'home' }
  }
}

mount(MyComponent, {
  global: {
    config: {
      globalProperties: {
        $router: mockRouter,
        $route: mockRouter.currentRoute.value
      }
    }
  }
})

Компонент не знает, что работает с моком, а тест полностью контролирует поведение навигации.


Изоляция provide/inject из плагинов

Многие плагины используют provide. В тестах нет необходимости устанавливать сам плагин — достаточно вручную задать global.provide:

mount(MyComponent, {
  global: {
    provide: {
      apiClient: {
        get: vi.fn().mockResolvedValue({ data: [] })
      }
    }
  }
})

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


Локальная регистрация директив и компонентов

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

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

или

mount(MyComponent, {
  global: {
    components: {
      BaseButton: {
        template: '<button><slot /></button>'
      }
    }
  }
})

Полноценный плагин не требуется.


Отключение поведения плагинов

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

export default {
  install(app, options = {}) {
    if (options.disableTracking) return
    // обычная логика
  }
}

В тесте:

mount(MyComponent, {
  global: {
    plugins: [[MyPlugin, { disableTracking: true }]]
  }
})

Vue Test Utils поддерживает передачу опций плагина в массиве.


Контроль побочных эффектов

Для полной изоляции важно:

  • сбрасывать моки (vi.resetAllMocks());
  • не использовать синглтоны в плагинах;
  • избегать обращения к window и document без явной необходимости;
  • не регистрировать плагины глобально вне тестов.

Каждый тест должен сам описывать своё окружение через mount.


Практическое правило

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