При тестировании компонентов Vue часто возникает необходимость изолировать компонент от глобального состояния, управляемого Vuex или Pinia. Это позволяет проверять поведение компонента независимо от внешних зависимостей, обеспечивая стабильность и предсказуемость тестов.
Основной способ изоляции — замена реального store на
мок-объект, который имитирует поведение настоящего
состояния и методов. В Vue Test Utils это делается через опцию
global.plugins при монтировании компонента:
import { mount } from '@vue/test-utils'
import MyComponent from '@/components/MyComponent.vue'
const mockStore = {
state: { count: 5 },
getters: { doubleCount: () => 10 },
dispatch: jest.fn(),
commit: jest.fn()
}
const wrapper = mount(MyComponent, {
global: {
mocks: {
$store: mockStore
}
}
})
В этом примере:
state и getters позволяют компоненту
получать данные, как если бы он работал с реальным store.dispatch и commit заменены на
jest-функции, что позволяет отслеживать вызовы действий и
мутаций без изменения настоящего состояния.Если компонент использует inject для доступа к store или
отдельным сервисам, можно подменять зависимости при
монтировании через global.provide:
const mockService = {
fetchData: jest.fn().mockResolvedValue({ items: [1,2,3] })
}
const wrapper = mount(MyComponent, {
global: {
provide: {
apiService: mockService
}
}
})
Это полностью изолирует компонент от внешнего сервиса и позволяет тестировать только логику компонента, без реальных запросов или состояния.
В Vue 3 с Vuex 4 или Pinia можно создавать отдельный store специально для теста:
import { createStore } from 'vuex'
const testStore = createStore({
state() {
return { count: 0 }
},
mutations: {
increment(state) { state.count++ }
}
})
const wrapper = mount(MyComponent, {
global: {
plugins: [testStore]
}
})
Такой подход полезен, когда компонент тесно связан с store, но нужны только отдельные ветки состояния. Можно создавать store минимальной конфигурации, без всего остального приложения.
Для тестирования поведения компонента при вызове store-методов используют подмену функций на мок-версии. Например:
const commitMock = jest.fn()
const dispatchMock = jest.fn()
const wrapper = mount(MyComponent, {
global: {
mocks: {
$store: {
state: { count: 0 },
commit: commitMock,
dispatch: dispatchMock
}
}
}
})
wrapper.find('button.increment').trigger('click')
expect(commitMock).toHaveBeenCalledWith('increment')
expect(dispatchMock).not.toHaveBeenCalled()
Такой подход позволяет:
При изоляции store важно помнить, что computed свойства и watch в компоненте продолжают реагировать на изменения мок-состояния:
const wrapper = mount(MyComponent, {
global: {
mocks: {
$store: { state: { count: 1 } }
}
}
})
expect(wrapper.vm.doubleCount).toBe(2) // если компонент вычисляет doubleCount через store
Чтобы протестировать реакцию на изменение состояния:
wrapper.vm.$store.state.count = 5
await wrapper.vm.$nextTick()
expect(wrapper.vm.doubleCount).toBe(10)
Это демонстрирует, что компонент корректно реагирует на изменение состояния, даже если оно полностью изолировано.
Иногда компонент использует несколько модулей store. Для изоляции их можно подменять отдельными модулями:
const moduleA = { state: { valueA: 1 }, getters: { doubleA: () => 2 } }
const moduleB = { state: { valueB: 3 }, getters: { doubleB: () => 6 } }
const wrapper = mount(MyComponent, {
global: {
mocks: {
$store: {
state: { moduleA, moduleB },
getters: { doubleA: moduleA.getters.doubleA, doubleB: moduleB.getters.doubleB }
}
}
}
})
Это позволяет изолированно тестировать каждый модуль, не создавая полноценный store с полной логикой приложения.
Изоляция компонента от store обеспечивает чистые, предсказуемые и быстрые unit-тесты, позволяя фокусироваться на логике самого компонента, а не на состоянии приложения.