name: vitest
description: Use for Vitest tests: mocking, coverage, fixtures, filtering, and setup. Not for non-Vitest testing frameworks.
Vitest
Vitest 3.x testing workflow — Jest-compatible API with Vite-native transforms.
Workflow
- Setup —
vitest.config.ts or inline in vite.config.ts with test key
- Write tests —
describe/it/expect (Jest-compatible), use .test.ts or .spec.ts
- Mock —
vi.mock() for modules, vi.fn() for functions, vi.spyOn() for methods
- Run —
vitest (watch), vitest run (CI), vitest run --reporter=verbose
- Coverage —
vitest run --coverage (V8 provider default)
Config Example
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./tests/setup.ts'],
coverage: { provider: 'v8', include: ['src/**'] },
},
})
If you already have a vite.config.ts, merge the test key in there instead of a separate file — Vitest reads either.
Mocking Example
import { describe, it, expect, vi, afterEach } from 'vitest'
import { fetchUser } from './api'
vi.mock('./api', () => ({
fetchUser: vi.fn(),
}))
describe('UserProfile', () => {
afterEach(() => {
vi.restoreAllMocks()
})
it('displays user name', async () => {
vi.mocked(fetchUser).mockResolvedValue({ name: 'Alice' })
const result = await fetchUser('1')
expect(result.name).toBe('Alice')
})
it('throws on network failure', async () => {
vi.mocked(fetchUser).mockRejectedValue(new Error('Network error'))
await expect(fetchUser('1')).rejects.toThrow('Network error')
})
})
Common Patterns
Timers — freeze time for interval/timeout logic:
vi.useFakeTimers()
myDebounce(() => {}, 300)
vi.advanceTimersByTime(300)
expect(callback).toHaveBeenCalledOnce()
vi.useRealTimers()
Spy on a method without replacing the module:
const spy = vi.spyOn(console, 'warn').mockImplementation(() => {})
doSomething()
expect(spy).toHaveBeenCalledWith('expected warning')
Snapshots — useful for generated output or complex objects:
expect(result).toMatchSnapshot()
expect(result).toMatchInlineSnapshot(`"foo"`)
Custom fixtures — share setup across tests without beforeEach duplication:
const test = base.extend<{ db: Database }>({
db: async ({}, use) => {
const db = await Database.connect(':memory:')
await use(db)
await db.close()
},
})
test('query returns rows', async ({ db }) => {
const rows = await db.query('SELECT 1')
expect(rows).toHaveLength(1)
})
Filtering tests at the command line:
vitest run -t "displays user"
vitest run src/api
vitest run --project web
Or in code: it.only(...), describe.skip(...).
Pitfalls