963 markdown files reformatted with the repository's pinned prettier so pnpm format:check covers the folded tree like every other repo file. The formatter's embedded-language pass also normalized code fences (TS semicolons, closed HTML tags in examples, lowercased CSS hex colors, one renumbered list that skipped an index). Alphanumeric token deltas vs the fold commit were audited file-by-file; all are formatter-equivalent markup normalizations plus the four sanitized skills.
6.2 KiB
6.2 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| Configure Pinia Testing with createTestingPinia and setActivePinia | HIGH | Missing Pinia configuration causes 'injection Symbol(pinia) not found' errors and failing tests | gotcha |
|
Configure Pinia Testing with createTestingPinia and setActivePinia
Impact: HIGH - Testing components or composables that use Pinia stores without proper configuration results in "[Vue warn]: injection Symbol(pinia) not found" errors. Tests will fail or behave unexpectedly.
Use @pinia/testing package with createTestingPinia for component tests and setActivePinia(createPinia()) for unit testing stores directly.
Task Checklist
- Install
@pinia/testingas a dev dependency - Use
createTestingPiniain component tests withglobal.plugins - Use
setActivePinia(createPinia())inbeforeEachfor store unit tests - Configure
createSpy: vi.fnwhen NOT usingglobals: truein Vitest - Initialize store inside each test to get fresh state
- Use
stubActions: falsewhen you need real action execution
Incorrect:
import { mount } from '@vue/test-utils';
import UserProfile from './UserProfile.vue';
// BAD: Missing Pinia - causes injection error
test('displays user name', () => {
const wrapper = mount(UserProfile); // ERROR: injection "Symbol(pinia)" not found
expect(wrapper.text()).toContain('John');
});
import { useUserStore } from '@/stores/user';
// BAD: No active Pinia instance
test('user store actions', () => {
const store = useUserStore(); // ERROR: no active Pinia
store.login('john', 'password');
});
Correct - Component Testing:
import { mount } from '@vue/test-utils';
import { createTestingPinia } from '@pinia/testing';
import { vi } from 'vitest';
import UserProfile from './UserProfile.vue';
import { useUserStore } from '@/stores/user';
// CORRECT: Provide testing pinia with stubbed actions
test('displays user name', () => {
const wrapper = mount(UserProfile, {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn, // Required if not using globals: true
initialState: {
user: { name: 'John', email: '[email protected]' },
},
}),
],
},
});
expect(wrapper.text()).toContain('John');
});
// CORRECT: Test with stubbed actions (default behavior)
test('calls logout action', async () => {
const wrapper = mount(UserProfile, {
global: {
plugins: [createTestingPinia({ createSpy: vi.fn })],
},
});
// Get store AFTER mounting with createTestingPinia
const store = useUserStore();
await wrapper.find('[data-testid="logout"]').trigger('click');
// Actions are stubbed and wrapped in spies
expect(store.logout).toHaveBeenCalled();
});
Correct - Store Unit Testing:
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { setActivePinia, createPinia } from 'pinia';
import { useUserStore } from '@/stores/user';
describe('User Store', () => {
beforeEach(() => {
// Create fresh Pinia instance for each test
setActivePinia(createPinia());
});
it('initializes with empty user', () => {
const store = useUserStore();
expect(store.user).toBeNull();
expect(store.isLoggedIn).toBe(false);
});
it('updates user on login', async () => {
const store = useUserStore();
// Real action executes - not stubbed
await store.login('john', 'password');
expect(store.user).toEqual({ name: 'John' });
expect(store.isLoggedIn).toBe(true);
});
it('clears user on logout', () => {
const store = useUserStore();
store.user = { name: 'John' }; // Set initial state
store.logout();
expect(store.user).toBeNull();
});
});
Testing with Real Actions vs Stubbed Actions
import { createTestingPinia } from '@pinia/testing';
// Stubbed actions (default) - for isolation
const wrapper = mount(Component, {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn,
// stubActions: true (default) - actions are mocked
}),
],
},
});
// Real actions - for integration testing
const wrapper = mount(Component, {
global: {
plugins: [
createTestingPinia({
createSpy: vi.fn,
stubActions: false, // Actions execute normally
}),
],
},
});
Mocking Specific Action Implementations
import { mount } from '@vue/test-utils';
import { createTestingPinia } from '@pinia/testing';
import { vi } from 'vitest';
import { useCartStore } from '@/stores/cart';
test('handles checkout failure', async () => {
const wrapper = mount(Checkout, {
global: {
plugins: [createTestingPinia({ createSpy: vi.fn })],
},
});
const cartStore = useCartStore();
// Mock specific action behavior
cartStore.checkout.mockRejectedValue(new Error('Payment failed'));
await wrapper.find('[data-testid="checkout"]').trigger('click');
await flushPromises();
expect(wrapper.find('.error').text()).toContain('Payment failed');
});
Spying on Actions with vi.spyOn
import { setActivePinia, createPinia } from 'pinia';
import { vi } from 'vitest';
import { useUserStore } from '@/stores/user';
test('tracks action calls', async () => {
setActivePinia(createPinia());
const store = useUserStore();
const loginSpy = vi.spyOn(store, 'login');
loginSpy.mockResolvedValue({ success: true });
await store.login('john', 'password');
expect(loginSpy).toHaveBeenCalledWith('john', 'password');
});
Testing Store $subscribe
import { setActivePinia, createPinia } from 'pinia';
import { useUserStore } from '@/stores/user';
test('subscription triggers on state change', () => {
setActivePinia(createPinia());
const store = useUserStore();
const callback = vi.fn();
store.$subscribe(callback);
store.user = { name: 'John' };
expect(callback).toHaveBeenCalled();
});