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>
266 lines
4.7 KiB
Markdown
266 lines
4.7 KiB
Markdown
---
|
|
name: mocking
|
|
description: Mock functions, modules, timers, and dates with vi utilities
|
|
---
|
|
|
|
# Mocking
|
|
|
|
## Mock Functions
|
|
|
|
```ts
|
|
import { expect, vi } from 'vitest'
|
|
|
|
// Create mock function
|
|
const fn = vi.fn()
|
|
fn('hello')
|
|
|
|
expect(fn).toHaveBeenCalled()
|
|
expect(fn).toHaveBeenCalledWith('hello')
|
|
|
|
// With implementation
|
|
const add = vi.fn((a, b) => a + b)
|
|
expect(add(1, 2)).toBe(3)
|
|
|
|
// Mock return values
|
|
fn.mockReturnValue(42)
|
|
fn.mockReturnValueOnce(1).mockReturnValueOnce(2)
|
|
fn.mockResolvedValue({ data: true })
|
|
fn.mockRejectedValue(new Error('fail'))
|
|
|
|
// Mock implementation
|
|
fn.mockImplementation((x) => x * 2)
|
|
fn.mockImplementationOnce(() => 'first call')
|
|
```
|
|
|
|
## Spying on Objects
|
|
|
|
```ts
|
|
const cart = {
|
|
getTotal: () => 100,
|
|
}
|
|
|
|
const spy = vi.spyOn(cart, 'getTotal')
|
|
cart.getTotal()
|
|
|
|
expect(spy).toHaveBeenCalled()
|
|
|
|
// Mock implementation
|
|
spy.mockReturnValue(200)
|
|
expect(cart.getTotal()).toBe(200)
|
|
|
|
// Restore original
|
|
spy.mockRestore()
|
|
```
|
|
|
|
## Module Mocking
|
|
|
|
```ts
|
|
// vi.mock is hoisted to top of file
|
|
vi.mock('./api', () => ({
|
|
fetchUser: vi.fn(() => ({ id: 1, name: 'Mock' })),
|
|
}))
|
|
|
|
import { fetchUser } from './api'
|
|
|
|
test('mocked module', () => {
|
|
expect(fetchUser()).toEqual({ id: 1, name: 'Mock' })
|
|
})
|
|
```
|
|
|
|
### Partial Mock
|
|
|
|
```ts
|
|
vi.mock('./utils', async (importOriginal) => {
|
|
const actual = await importOriginal()
|
|
return {
|
|
...actual,
|
|
specificFunction: vi.fn(),
|
|
}
|
|
})
|
|
```
|
|
|
|
### Auto-mock with Spy
|
|
|
|
```ts
|
|
// Keep implementation but spy on calls
|
|
vi.mock('./calculator', { spy: true })
|
|
|
|
import { add } from './calculator'
|
|
|
|
test('spy on module', () => {
|
|
const result = add(1, 2) // Real implementation
|
|
expect(result).toBe(3)
|
|
expect(add).toHaveBeenCalledWith(1, 2)
|
|
})
|
|
```
|
|
|
|
### Manual Mocks (__mocks__)
|
|
|
|
```
|
|
src/
|
|
__mocks__/
|
|
axios.ts # Mocks 'axios'
|
|
api/
|
|
__mocks__/
|
|
client.ts # Mocks './client'
|
|
client.ts
|
|
```
|
|
|
|
```ts
|
|
// Just call vi.mock with no factory
|
|
vi.mock('axios')
|
|
vi.mock('./api/client')
|
|
```
|
|
|
|
## Dynamic Mocking (vi.doMock)
|
|
|
|
Not hoisted - use for dynamic imports:
|
|
|
|
```ts
|
|
test('dynamic mock', async () => {
|
|
vi.doMock('./config', () => ({
|
|
apiUrl: 'http://test.local',
|
|
}))
|
|
|
|
const { apiUrl } = await import('./config')
|
|
expect(apiUrl).toBe('http://test.local')
|
|
|
|
vi.doUnmock('./config')
|
|
})
|
|
```
|
|
|
|
## Mock Timers
|
|
|
|
```ts
|
|
import { afterEach, beforeEach, vi } from 'vitest'
|
|
|
|
beforeEach(() => {
|
|
vi.useFakeTimers()
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers()
|
|
})
|
|
|
|
test('timers', () => {
|
|
const fn = vi.fn()
|
|
setTimeout(fn, 1000)
|
|
|
|
expect(fn).not.toHaveBeenCalled()
|
|
|
|
vi.advanceTimersByTime(1000)
|
|
expect(fn).toHaveBeenCalled()
|
|
})
|
|
|
|
// Other timer methods
|
|
vi.runAllTimers() // Run all pending timers
|
|
vi.runOnlyPendingTimers() // Run only currently pending
|
|
vi.advanceTimersToNextTimer() // Advance to next timer
|
|
```
|
|
|
|
### Async Timer Methods
|
|
|
|
```ts
|
|
test('async timers', async () => {
|
|
vi.useFakeTimers()
|
|
|
|
let resolved = false
|
|
setTimeout(() => Promise.resolve().then(() => { resolved = true }), 100)
|
|
|
|
await vi.advanceTimersByTimeAsync(100)
|
|
expect(resolved).toBe(true)
|
|
})
|
|
```
|
|
|
|
## Mock Dates
|
|
|
|
```ts
|
|
vi.setSystemTime(new Date('2024-01-01'))
|
|
expect(new Date().getFullYear()).toBe(2024)
|
|
|
|
vi.useRealTimers() // Restore
|
|
```
|
|
|
|
## Mock Globals
|
|
|
|
```ts
|
|
vi.stubGlobal('fetch', vi.fn(() =>
|
|
Promise.resolve({ json: () => ({ data: 'mock' }) })
|
|
))
|
|
|
|
// Restore
|
|
vi.unstubAllGlobals()
|
|
```
|
|
|
|
## Mock Environment Variables
|
|
|
|
```ts
|
|
vi.stubEnv('API_KEY', 'test-key')
|
|
expect(import.meta.env.API_KEY).toBe('test-key')
|
|
|
|
// Restore
|
|
vi.unstubAllEnvs()
|
|
```
|
|
|
|
## Clearing Mocks
|
|
|
|
```ts
|
|
const fn = vi.fn()
|
|
fn()
|
|
|
|
fn.mockClear() // Clear call history
|
|
fn.mockReset() // Clear history + implementation
|
|
fn.mockRestore() // Restore original (for spies)
|
|
|
|
// Global
|
|
vi.clearAllMocks()
|
|
vi.resetAllMocks()
|
|
vi.restoreAllMocks()
|
|
```
|
|
|
|
## Config Auto-Reset
|
|
|
|
```ts
|
|
// vitest.config.ts
|
|
defineConfig({
|
|
test: {
|
|
clearMocks: true, // Clear before each test
|
|
mockReset: true, // Reset before each test
|
|
restoreMocks: true, // Restore after each test
|
|
unstubEnvs: true, // Restore env vars
|
|
unstubGlobals: true, // Restore globals
|
|
},
|
|
})
|
|
```
|
|
|
|
## Hoisted Variables for Mocks
|
|
|
|
```ts
|
|
const mockFn = vi.hoisted(() => vi.fn())
|
|
|
|
vi.mock('./module', () => ({
|
|
getData: mockFn,
|
|
}))
|
|
|
|
import { getData } from './module'
|
|
|
|
test('hoisted mock', () => {
|
|
mockFn.mockReturnValue('test')
|
|
expect(getData()).toBe('test')
|
|
})
|
|
```
|
|
|
|
## Key Points
|
|
|
|
- `vi.mock` is hoisted - called before imports
|
|
- Use `vi.doMock` for dynamic, non-hoisted mocking
|
|
- Always restore mocks to avoid test pollution
|
|
- Use `{ spy: true }` to keep implementation but track calls
|
|
- `vi.hoisted` lets you reference variables in mock factories
|
|
|
|
<!--
|
|
Source references:
|
|
- https://vitest.dev/guide/mocking.html
|
|
- https://vitest.dev/api/vi.html
|
|
-->
|