Pinia provides a testing helper that makes it easy to test stores and components that use them.
Setup
bashnpm install -D @pinia/testing
Testing Stores Directly
typescriptimport { setActivePinia, createPinia } from 'pinia' import { useCounterStore } from '@/stores/counter' describe('Counter Store', () => { beforeEach(() => { setActivePinia(createPinia()) }) it('has initial state', () => { const store = useCounterStore() expect(store.count).toBe(0) }) it('increments count', () => { const store = useCounterStore() store.increment() expect(store.count).toBe(1) }) it('computes double', () => { const store = useCounterStore() store.count = 5 expect(store.double).toBe(10) }) })
Testing Components with Stores
typescriptimport { mount } from '@vue/test-utils' import { createTestingPinia } from '@pinia/testing' import { vi } from 'vitest' import Counter from './Counter.vue' import { useCounterStore } from '@/stores/counter' describe('Counter Component', () => { it('renders count from store', () => { const wrapper = mount(Counter, { global: { plugins: [ createTestingPinia({ initialState: { counter: { count: 10 } } }) ] } }) expect(wrapper.text()).toContain('10') }) it('calls increment action', async () => { const wrapper = mount(Counter, { global: { plugins: [createTestingPinia()] } }) const store = useCounterStore() await wrapper.find('button').trigger('click') // Actions are stubbed by default expect(store.increment).toHaveBeenCalled() }) })
createTestingPinia Options
typescriptcreateTestingPinia({ // Initial state for stores initialState: { counter: { count: 100 }, user: { name: 'Test User' } }, // Stub actions (default: true) stubActions: true, // Custom spy function (default: vi.fn) createSpy: vi.fn, // Plugins to use plugins: [piniaPluginPersistedstate] })
Testing Real Actions
typescriptdescribe('With real actions', () => { it('executes increment', async () => { const wrapper = mount(Counter, { global: { plugins: [ createTestingPinia({ stubActions: false // Run real actions }) ] } }) const store = useCounterStore() expect(store.count).toBe(0) await wrapper.find('button').trigger('click') expect(store.count).toBe(1) // Actually incremented }) })
Mocking Store Getters
typescriptit('shows loading state', () => { const wrapper = mount(UserList, { global: { plugins: [ createTestingPinia({ initialState: { users: { users: [], loading: true } } }) ] } }) expect(wrapper.find('.loading').exists()).toBe(true) })
Testing Async Actions
typescriptimport { flushPromises } from '@vue/test-utils' describe('Async Store', () => { it('fetches users', async () => { setActivePinia(createPinia()) const store = useUserStore() // Mock fetch global.fetch = vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve([{ id: 1, name: 'Alice' }]) }) await store.fetchUsers() expect(store.users).toHaveLength(1) expect(store.users[0].name).toBe('Alice') }) })
Testing Store Subscriptions
typescriptit('subscribes to changes', () => { setActivePinia(createPinia()) const store = useCounterStore() const callback = vi.fn() store.$subscribe(callback) store.increment() expect(callback).toHaveBeenCalled() })