Files
stack/v1/packages/mosaic/framework/skills/pinia/references/best-practices-testing.md
T

3.8 KiB

name, description
name description
testing Unit testing stores and components with @pinia/testing

Testing Stores

Unit Testing Stores

Create a fresh pinia instance for each test:

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

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:

npm i -D @pinia/testing

Use createTestingPinia():

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:

const wrapper = mount(Counter, {
  global: {
    plugins: [
      createTestingPinia({
        initialState: {
          counter: { n: 20 }, // Store name → initial state
        },
      }),
    ],
  },
});

Action Stubbing

Execute Real Actions

createTestingPinia({ stubActions: false });

Selective Stubbing

// 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

import type { Mock } from 'vitest';

// After getting store
store.someAction.mockResolvedValue('mocked value');

Mocking Getters

Getters are writable in tests:

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:

import { vi } from 'vitest';

createTestingPinia({
  createSpy: vi.fn,
});

With Sinon:

import sinon from 'sinon';

createTestingPinia({
  createSpy: sinon.spy,
});

Pinia Plugins in Tests

Pass plugins to createTestingPinia():

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

import type { Mock } from 'vitest';
import type { Store, StoreDefinition } from 'pinia';

function mockedStore<TStoreDef extends () => unknown>(
  useStore: TStoreDef,
): TStoreDef extends StoreDefinition<infer Id, infer State, infer Getters, infer Actions>
  ? Store<
      Id,
      State,
      Record<string, never>,
      {
        [K in keyof Actions]: Actions[K] extends (...args: any[]) => any
          ? Mock<Actions[K]>
          : Actions[K];
      }
    >
  : ReturnType<TStoreDef> {
  return useStore() as any;
}

// Usage
const store = mockedStore(useSomeStore);
store.someAction.mockResolvedValue('value'); // Typed!

E2E Tests

No special handling needed - Pinia works normally.