Pinia является официальной библиотекой управления состоянием для Vue 3 и полностью заменяет Vuex. Тестирование Pinia stores играет ключевую роль в обеспечении стабильности и предсказуемости поведения приложения. Рассмотрим детально подходы и практики для юнит-тестирования stores.
Для тестирования Pinia stores обычно используется комбинация Vue Test Utils и тестового раннера, например Vitest или Jest. Основные зависимости:
npm install @vue/test-utils pinia vitest
В тестах необходимо создавать изолированные экземпляры Pinia для
предотвращения конфликтов между тестами. Это достигается через функцию
createPinia().
import { createPinia, setActivePinia } from 'pinia'
beforeEach(() => {
setActivePinia(createPinia())
})
setActivePinia задаёт активный store, к которому будут
обращаться вызовы useStore() внутри тестов.
Пример простого store:
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount: (state) => state.count * 2
},
actions: {
increment() {
this.count++
},
incrementBy(value) {
this.count += value
}
}
})
Для тестирования необходимо импортировать этот store и создавать его экземпляры в тестах.
import { useCounterStore } from '@/stores/counter'
Юнит-тест состояния проверяет начальные значения и изменения через actions.
import { describe, it, expect, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useCounterStore } from '@/stores/counter'
describe('Counter Store', () => {
let store
beforeEach(() => {
setActivePinia(createPinia())
store = useCounterStore()
})
it('должен иметь начальное состояние count = 0', () => {
expect(store.count).toBe(0)
})
it('increment увеличивает count на 1', () => {
store.increment()
expect(store.count).toBe(1)
})
it('incrementBy корректно увеличивает count на указанное значение', () => {
store.incrementBy(5)
expect(store.count).toBe(5)
})
})
Ключевой момент: beforeEach
гарантирует, что каждый тест получает новый изолированный экземпляр
store.
Геттеры в Pinia — это вычисляемые свойства. Их тестирование похоже на проверку методов.
it('doubleCount возвращает удвоенное значение count', () => {
store.count = 3
expect(store.doubleCount).toBe(6)
})
Геттеры автоматически обновляются при изменении состояния, поэтому их тестирование должно учитывать разные сценарии изменения state.
Actions могут быть асинхронными, например при работе с API. Для таких
случаев важно использовать async/await и мокать сетевые
запросы.
import axios from 'axios'
vi.mock('axios')
it('fetchData обновляет state', async () => {
const data = { value: 10 }
axios.get.mockResolvedValue({ data })
store.count = 0
await store.fetchData()
expect(store.count).toBe(10)
})
Замечание: использование vi.mock или
jest.mock позволяет изолировать store от внешних
зависимостей.
Pinia предоставляет возможность подписки на изменения state с помощью
$subscribe. В тестах это позволяет проверять реакции на
изменения.
it('срабатывает подписка при изменении state', () => {
const callback = vi.fn()
store.$subscribe(callback)
store.increment()
expect(callback).toHaveBeenCalled()
})
Подписки полезны для тестирования побочных эффектов, например логирования или синхронизации состояния с локальным хранилищем.
Иногда store зависит от других store или внешних сервисов. Для корректного тестирования используют мокирование зависимостей:
import { useOtherStore } from '@/stores/other'
vi.mock('@/stores/other', () => ({
useOtherStore: () => ({ value: 42 })
}))
it('использует mocked другой store', () => {
const otherStore = useOtherStore()
expect(otherStore.value).toBe(42)
})
Это позволяет полностью изолировать store и сосредоточиться на его собственной логике.
Для каждого store создавать отдельный файл с тестами, например
counter.spec.js.
Использовать beforeEach для сброса состояния и
создания нового экземпляра Pinia.
Покрывать тестами:
Мокировать все внешние зависимости для чистоты юнит-тестов.
Тщательное тестирование Pinia stores обеспечивает стабильность логики состояния и позволяет безопасно рефакторить приложение, минимизируя риск ошибок в сложных компонентах Vue.