Store テスト
13.4.1. createTestingPinia
テスト環境では @pinia/testing の createTestingPinia() を使用してテスト用の Pinia インスタンスを生成します。
createTestingPinia()はすべての action を自動的にモックします。stubActions: falseを指定すると実際の action が実行されます。initialStateで Store の初期状態をオーバーライドすることができます。
typescript
import { createTestingPinia } from '@pinia/testing'
import { vi } from 'vitest'
// 基本的な使用方法: すべての action が自動モックされます。
const pinia = createTestingPinia({
createSpy: vi.fn,
})
// action を実際に実行する場合
const pinia = createTestingPinia({
createSpy: vi.fn,
stubActions: false,
})
// 初期状態を指定する場合
const pinia = createTestingPinia({
createSpy: vi.fn,
initialState: {
user: { user: { id: 1, name: 'テストユーザー' }, loading: false },
cart: { items: [{ id: 1, name: '商品A', price: 10000, quantity: 2 }] },
},
})13.4.2. Store 単体テスト
Store をコンポーネントなしで独立してテストする場合は setActivePinia() を使用します。
typescript
// src/stores/__tests__/useCartStore.test.ts
import { describe, it, expect, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useCartStore } from '../useCartStore'
describe('useCartStore', () => {
beforeEach(() => {
setActivePinia(createPinia())
})
it('初期状態は空の配列でなければなりません', () => {
const store = useCartStore()
expect(store.items).toEqual([])
expect(store.isEmpty).toBe(true)
})
it('項目追加時に totalPrice を正しく計算しなければなりません', () => {
const store = useCartStore()
store.items.push({ id: 1, name: '商品A', price: 5000, quantity: 3 })
expect(store.totalPrice).toBe(15000)
expect(store.itemCount).toBe(1)
})
it('resetState 呼び出し時に初期状態に復元されなければなりません', () => {
const store = useCartStore()
store.items.push({ id: 1, name: '商品A', price: 5000, quantity: 1 })
store.resetState()
expect(store.items).toEqual([])
})
})beforeEachで毎回新しい Pinia インスタンスを生成してテスト間の状態汚染を防止します。- getter は状態変更後の戻り値を検証します。
13.4.3. コンポーネント内の Store テスト
コンポーネントで Store を使用する場合、mount の global.plugins に createTestingPinia() を注入します。
typescript
// src/components/__tests__/UserProfile.test.ts
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import { vi } from 'vitest'
import { createTestingPinia } from '@pinia/testing'
import UserProfile from '../UserProfile.vue'
import { useUserStore } from '@/stores/useUserStore'
describe('UserProfile', () => {
it('ユーザー名を表示しなければなりません', () => {
const wrapper = mount(UserProfile, {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn,
initialState: {
user: {
user: { id: 1, name: '山田太郎' },
loading: false,
},
},
}),
],
},
})
expect(wrapper.text()).toContain('山田太郎')
})
it('マウント時に loadCurrentUser を呼び出さなければなりません', () => {
const wrapper = mount(UserProfile, {
global: {
plugins: [createTestingPinia({ createSpy: vi.fn })],
},
})
const userStore = useUserStore()
expect(userStore.loadCurrentUser).toHaveBeenCalledOnce()
})
})initialStateのキーはdefineStoreに渡した Store ID と一致しなければなりません。- コンポーネントマウント後に
useUserStore()を呼び出すとテスト用の Store インスタンスが返されます。
13.4.4. モッキングパターン
API 呼び出しのモッキング
Store 内部の API 呼び出しは vi.mock() でモックします。
typescript
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useUserStore } from '../useUserStore'
import { fetchCurrentUser } from '@/api/user'
vi.mock('@/api/user', () => ({
fetchCurrentUser: vi.fn(),
}))
describe('useUserStore - API 連携', () => {
beforeEach(() => {
setActivePinia(createPinia())
vi.clearAllMocks()
})
it('loadCurrentUser 成功時にユーザー情報を保存しなければなりません', async () => {
const mockUser = { id: 1, name: 'テストユーザー' }
vi.mocked(fetchCurrentUser).mockResolvedValue(mockUser)
const store = useUserStore()
await store.loadCurrentUser()
expect(store.user).toEqual(mockUser)
expect(store.isLoggedIn).toBe(true)
expect(store.loading).toBe(false)
})
})Action のモッキング
createTestingPinia() を使用するとすべての action が自動モックされます。特定の action の戻り値を指定するには mockResolvedValue() を使用します。
typescript
const pinia = createTestingPinia({ createSpy: vi.fn })
const userStore = useUserStore()
vi.mocked(userStore.loadCurrentUser).mockResolvedValue(undefined)stubActions: true(デフォルト)では action の呼び出し有無のみを検証します。- action の実際のロジックをテストするには
stubActions: falseを指定するか、Store 単体テストを別途作成します。