Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
229 lines
6.1 KiB
Markdown
229 lines
6.1 KiB
Markdown
---
|
|
title: Configure Pinia Testing with createTestingPinia and setActivePinia
|
|
impact: HIGH
|
|
impactDescription: Missing Pinia configuration causes 'injection Symbol(pinia) not found' errors and failing tests
|
|
type: gotcha
|
|
tags: [vue3, testing, pinia, vitest, store, mocking, createTestingPinia]
|
|
---
|
|
|
|
# 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/testing` as a dev dependency
|
|
- [ ] Use `createTestingPinia` in component tests with `global.plugins`
|
|
- [ ] Use `setActivePinia(createPinia())` in `beforeEach` for store unit tests
|
|
- [ ] Configure `createSpy: vi.fn` when NOT using `globals: true` in Vitest
|
|
- [ ] Initialize store inside each test to get fresh state
|
|
- [ ] Use `stubActions: false` when you need real action execution
|
|
|
|
**Incorrect:**
|
|
```javascript
|
|
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')
|
|
})
|
|
```
|
|
|
|
```javascript
|
|
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:**
|
|
```javascript
|
|
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: 'john@example.com' }
|
|
}
|
|
})
|
|
]
|
|
}
|
|
})
|
|
|
|
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:**
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
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
|
|
|
|
```javascript
|
|
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()
|
|
})
|
|
```
|
|
|
|
## Reference
|
|
- [Pinia Testing Guide](https://pinia.vuejs.org/cookbook/testing.html)
|
|
- [@pinia/testing Package](https://www.npmjs.com/package/@pinia/testing)
|
|
- [Vue Test Utils - Plugins](https://test-utils.vuejs.org/guide/advanced/plugins.html)
|