Моки глобальных свойств

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

Глобальные свойства в Vue 3

В Vue 3 глобальные свойства регистрируются через app.config.globalProperties. Например, для регистрации метода notify:

const app = createApp(App)
app.config.globalProperties.$notify = (message) => console.log(message)

После этого любой компонент может вызвать:

this.$notify('Сообщение')

При тестировании вызов this.$notify может нежелательно выводить сообщения в консоль или вызывать побочные эффекты. Решение — заменить его мок-версией.

Использование Vue Test Utils для моков

Vue Test Utils предоставляет возможность передавать глобальные свойства через объект global при монтировании компонента:

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

const $notify = jest.fn()

const wrapper = mount(MyComponent, {
  global: {
    config: {
      globalProperties: {
        $notify
      }
    }
  }
})

wrapper.find('button').trigger('click')
expect($notify).toHaveBeenCalledWith('Сообщение')

Ключевые моменты:

  • global.config.globalProperties позволяет определить любые свойства, доступные компоненту через this.
  • Можно использовать jest.fn() или любую другую функцию-мок для отслеживания вызовов.
  • Моки изолируют тест от глобальных эффектов приложения.

Моки Vue Router и Vuex

Для компонентов, использующих глобальные объекты вроде router или store, можно создать мок-версии:

const $router = {
  push: jest.fn()
}

const wrapper = mount(MyComponent, {
  global: {
    config: {
      globalProperties: {
        $router
      }
    }
  }
})

wrapper.vm.$router.push('/home')
expect($router.push).toHaveBeenCalledWith('/home')

Для Vuex и Pinia можно использовать подход с provide или монтировать store отдельно, но замена глобальных свойств через globalProperties обеспечивает быстрый и изолированный контроль поведения.

Моки с доступом к реактивным объектам

Если глобальное свойство возвращает реактивный объект, важно использовать reactive из Vue, чтобы сохранить реактивность в тесте:

import { reactive } from 'vue'

const $user = reactive({
  name: 'Test User',
  loggedIn: true
})

const wrapper = mount(MyComponent, {
  global: {
    config: {
      globalProperties: {
        $user
      }
    }
  }
})

expect(wrapper.vm.$user.name).toBe('Test User')
wrapper.vm.$user.name = 'New Name'
expect(wrapper.vm.$user.name).toBe('New Name')

Это позволяет тестировать компоненты с реактивными глобальными данными без необходимости поднимать весь контекст приложения.

Перезапись существующих глобальных свойств

При необходимости временно изменять глобальное свойство, можно использовать локальный объект globalProperties в конкретном тесте, не трогая реальный экземпляр приложения:

const originalNotify = MyComponent.config.globalProperties?.$notify
const $notify = jest.fn()

const wrapper = mount(MyComponent, {
  global: {
    config: {
      globalProperties: {
        $notify
      }
    }
  }
})

// После теста можно вернуть оригинал
if (originalNotify) {
  MyComponent.config.globalProperties.$notify = originalNotify
}

Практические рекомендации

  • Всегда использовать локальные моки для каждого теста, чтобы не создавать зависимостей между тестами.
  • Моки функций лучше реализовывать через jest.fn(), sinon.stub() или аналогичные инструменты.
  • Для реактивных глобальных свойств применять reactive или ref, чтобы компонент корректно реагировал на изменения.
  • Для сложных глобальных объектов можно создавать фабрики моков, возвращающие полный набор методов и свойств, имитирующих реальный объект.

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