--- name: testing description: Unit testing stores and components with @pinia/testing --- # Testing Stores ## Unit Testing Stores Create a fresh pinia instance for each test: ```ts import { setActivePinia, createPinia } from 'pinia' import { useCounterStore } from '../src/stores/counter' describe('Counter Store', () => { beforeEach(() => { setActivePinia(createPinia()) }) it('increments', () => { const counter = useCounterStore() expect(counter.n).toBe(0) counter.increment() expect(counter.n).toBe(1) }) }) ``` ### With Plugins ```ts import { setActivePinia, createPinia } from 'pinia' import { createApp } from 'vue' import { somePlugin } from '../src/stores/plugin' const app = createApp({}) beforeEach(() => { const pinia = createPinia().use(somePlugin) app.use(pinia) setActivePinia(pinia) }) ``` ## Testing Components Install `@pinia/testing`: ```bash npm i -D @pinia/testing ``` Use `createTestingPinia()`: ```ts import { mount } from '@vue/test-utils' import { createTestingPinia } from '@pinia/testing' import { useSomeStore } from '@/stores/myStore' const wrapper = mount(Counter, { global: { plugins: [createTestingPinia()], }, }) const store = useSomeStore() // Manipulate state directly store.name = 'new name' store.$patch({ name: 'new name' }) // Actions are stubbed by default store.someAction() expect(store.someAction).toHaveBeenCalledTimes(1) ``` ## Initial State Set initial state for tests: ```ts const wrapper = mount(Counter, { global: { plugins: [ createTestingPinia({ initialState: { counter: { n: 20 }, // Store name → initial state }, }), ], }, }) ``` ## Action Stubbing ### Execute Real Actions ```ts createTestingPinia({ stubActions: false }) ``` ### Selective Stubbing ```ts // Only stub specific actions createTestingPinia({ stubActions: ['increment', 'reset'], }) // Or use a function createTestingPinia({ stubActions: (actionName, store) => { if (actionName.startsWith('set')) return true return false }, }) ``` ### Mock Action Return Values ```ts import type { Mock } from 'vitest' // After getting store store.someAction.mockResolvedValue('mocked value') ``` ## Mocking Getters Getters are writable in tests: ```ts const pinia = createTestingPinia() const counter = useCounterStore(pinia) counter.double = 3 // Override computed value // Reset to default behavior counter.double = undefined counter.double // Now computed normally ``` ## Custom Spy Function If not using Jest/Vitest with globals: ```ts import { vi } from 'vitest' createTestingPinia({ createSpy: vi.fn, }) ``` With Sinon: ```ts import sinon from 'sinon' createTestingPinia({ createSpy: sinon.spy, }) ``` ## Pinia Plugins in Tests Pass plugins to `createTestingPinia()`: ```ts import { somePlugin } from '../src/stores/plugin' createTestingPinia({ stubActions: false, plugins: [somePlugin], }) ``` **Don't use** `testingPinia.use(MyPlugin)` - pass plugins in options. ## Type-Safe Mocked Store ```ts import type { Mock } from 'vitest' import type { Store, StoreDefinition } from 'pinia' function mockedStore unknown>( useStore: TStoreDef ): TStoreDef extends StoreDefinition ? Store, { [K in keyof Actions]: Actions[K] extends (...args: any[]) => any ? Mock : Actions[K] }> : ReturnType { return useStore() as any } // Usage const store = mockedStore(useSomeStore) store.someAction.mockResolvedValue('value') // Typed! ``` ## E2E Tests No special handling needed - Pinia works normally.