Learn Pinia - Testing with @pinia/testing
Episode 17 of 23

Learn Pinia - Testing with @pinia/testing

A healthy store is a testable store. This episode covers createTestingPinia for a mock pinia, resetting state between tests, mocking actions and APIs with vi.fn and MSW, testing getters, and component testing with Vue Test Utils and Vitest.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Introduction

Code without tests is a time bomb — especially state management used by many components. To answer this need, the Pinia team provides @pinia/testing: a utility for creating a mock pinia that makes testing stores and components easy.

Episode 17 covers Pinia testing thoroughly: createTestingPinia, resetting state between tests, mocking actions and APIs, testing getters, and component testing with Vue Test Utils and Vitest.

Installation and createTestingPinia

Install the testing tools first:

Install testing tooling
npm i -D vitest @vue/test-utils @pinia/testing

For store unit tests, enable a mock pinia with createTestingPinia:

JSTesting pinia setup
import { setActivePinia } from 'pinia'
import { createTestingPinia } from '@pinia/testing'
import { beforeEach } from 'vitest'
 
beforeEach(() => {
  setActivePinia(createTestingPinia({ createSpy: vi.fn }))
})

createTestingPinia({ createSpy: vi.fn }) creates a pinia where every action automatically becomes a spy. In each test, setActivePinia guarantees clean state — no leakage between tests.

Testing Stores and Actions

Because actions become spies, you can check calls without running the real side effects:

JSTest the counter action
import { useCounterStore } from '@/stores/counter'
import { createTestingPinia } from '@pinia/testing'
 
it('calls the increment action', () => {
  const pinia = createTestingPinia({ createSpy: vi.fn })
  const store = useCounterStore(pinia)
 
  store.increment()
  expect(store.increment).toHaveBeenCalledTimes(1)
})

useCounterStore(pinia) explicitly uses the mock pinia instance. The increment action doesn't actually add to state — it becomes a spy, so the test focuses on call behavior.

Initializing Initial State

To test logic that depends on specific state, pass initialState:

JSInitial state in testing pinia
const pinia = createTestingPinia({
  initialState: {
    cart: { items: [{ name: 'Coffee', price: 15000, qty: 2 }] },
  },
})
 
const store = useCartStore(pinia)
expect(store.totalPrice).toBe(30000)

initialState: { cart: {...} } fills the cart store's state before the test runs. This is the most convenient way to test getters with prepared data.

Mocking APIs with vi.fn

For async actions, mock fetch so the network is never touched:

JSMock fetch in an action
it('fills the profile from the API', async () => {
  vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
    ok: true,
    json: () => Promise.resolve({ name: 'Arman' }),
  }))
 
  const store = useUserStore()
  await store.fetchProfile()
  expect(store.profile?.name).toBe('Arman')
})

vi.stubGlobal('fetch', ...) replaces the global fetch with a mock. For larger projects, use MSW to handle many endpoints realistically.

Component Testing

Finally, test components that use a store with Vue Test Utils:

JSComponent test with testing pinia
import { mount } from '@vue/test-utils'
import CounterView from '@/components/CounterView.vue'
 
it('renders the value from the store', () => {
  const wrapper = mount(CounterView, {
    global: { plugins: [createTestingPinia()] },
  })
  expect(wrapper.text()).toContain('Value: 0')
})

global: { plugins: [createTestingPinia()] } injects the mock pinia into every component under test. The component runs exactly like in production, but all store actions are controlled.

Tip

Focus tests on behavior: action calls, getter results, and what's rendered. Don't test Vue implementation details that the framework already handles.

Closing

Episode 17 equips you with complete testing tooling. You can now create a mock pinia with createTestingPinia, reset state between tests, initialize state, mock APIs and actions, and do component testing.

Key takeaways:

  • createTestingPinia({ createSpy: vi.fn }) turns actions into spies.
  • setActivePinia in beforeEach keeps state clean.
  • initialState fills initial state for testing getters.
  • Mock fetch with vi.fn or MSW for async actions.
  • Component testing via global.plugins in Vue Test Utils.
  • Test behavior, not implementation details.

In the next episode, episode 18, we'll discuss custom plugins and advanced lifecycle — using $onAction for tracking, subscribes that write to JSON, and global plugins for analytics and error handlers. This refines the plugin capabilities from episode 8.

Learn Pinia - Testing with @pinia/testing | Learning Pinia