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 단위 테스트를 별도로 작성합니다.