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>
213 lines
3.7 KiB
Markdown
213 lines
3.7 KiB
Markdown
---
|
|
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<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.
|
|
|
|
<!--
|
|
Source references:
|
|
- https://pinia.vuejs.org/cookbook/testing.html
|
|
-->
|