Vitest
Anthony FuCommunityCreated by Anthony Fu
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Vitest is a testing framework built for the Vite ecosystem. It provides a Jest-compatible API and supports ESM, TypeScript, and JSX by default. It shares configuration, transformers, and plugins with Vite applications, allowing for integrated testing workflows. The framework includes features for mocking, snapshot testing, code coverage, and multi-threaded test execution.
What it does
- Provides a Jest-compatible API for writing and running unit tests
- Supports native ESM, TypeScript, and JSX without additional configuration
- Enables mocking of functions, modules, timers, and dates
- Offers built-in code coverage reporting using V8 or Istanbul
- Supports multi-project workspaces and varied test environments like Node, jsdom, and happy-dom
- Includes utilities for snapshot testing, test filtering, and benchmarking
When to use it
- When you need a testing framework that integrates directly with your Vite configuration
- When migrating existing test suites that rely on Jest-compatible APIs
- When you require specific test environments like jsdom or happy-dom for DOM-related testing
- When managing complex test setups across monorepos or multiple projects
Advantages
- Shares configuration and plugins with Vite projects
- Supports parallel test execution via multi-threaded workers
- Provides flexible test environment configuration per file or project
- Includes built-in support for snapshot testing and mocking utilities
Usage examples
- “How do I configure Vitest to use the jsdom environment for my tests?”
- The agent provides the vitest.config.ts configuration snippet using the environment: 'jsdom' property and explains how to install the necessary jsdom package.
- “Can I run different test configurations in the same Vitest process?”
- The agent explains how to use the projects field in the vitest.config.ts file to define multiple test suites with different environments or include patterns.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/antfu/vitest/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip vitest.zip -d .agents/skills
Documentation by Anthony Fu
Vitest is a next-generation testing framework powered by Vite. It provides a Jest-compatible API with native ESM, TypeScript, and JSX support out of the box. Vitest shares the same config, transformers, resolvers, and plugins with your Vite app.
Key Features:
Vite-native: Uses Vite's transformation pipeline for fast HMR-like test updates
Jest-compatible: Drop-in replacement for most Jest test suites
Smart watch mode: Only reruns affected tests based on module graph
Native ESM, TypeScript, JSX support without configuration
Multi-threaded workers for parallel test execution
Built-in coverage via V8 or Istanbul
Snapshot testing, mocking, and spy utilities
The skill is based on Vitest 5.1.0-beta.1, generated at 2026-10-23.
Core
| Topic | Description | Reference |
|---|---|---|
| Configuration | Vitest and Vite config integration, defineConfig usage | core-config |
| CLI | Command line interface, commands and options | core-cli |
| Test API | test/it function, modifiers like skip, only, concurrent | core-test-api |
| Describe API | describe/suite for grouping tests and nested suites | core-describe |
| Expect API | Assertions with toBe, toEqual, matchers and asymmetric matchers | core-expect |
| Hooks | beforeEach, afterEach, beforeAll, afterAll, aroundEach | core-hooks |
Features
| Topic | Description | Reference |
|---|---|---|
| Mocking | Mock functions, modules, timers, dates with vi utilities | features-mocking |
| Snapshots | Snapshot testing with toMatchSnapshot and inline snapshots | features-snapshots |
| Coverage | Code coverage with V8 or Istanbul providers | features-coverage |
| Test Context | Test fixtures, context.expect, test.extend for custom fixtures | features-context |
| Concurrency | Concurrent tests, parallel execution, sharding | features-concurrency |
| Filtering | Filter tests by name, file patterns, tags | features-filtering |
| Test Tags | Label tests with tags to filter runs and apply shared options | features-test-tags |
| Reporters | Built-in reporters, default selection, CI/output config | features-reporters |
| Benchmarking | Write benchmarks with the bench fixture (Tinybench) | features-benchmarking |
Advanced
| Topic | Description | Reference |
|---|---|---|
| Vi Utilities | vi helper: mock, spyOn, fake timers, hoisted, waitFor | advanced-vi |
| Environments | Test environments: node, jsdom, happy-dom, custom | advanced-environments |
| Type Testing | Type-level testing with expectTypeOf and assertType | advanced-type-testing |
| Projects | Multi-project workspaces, different configs per project | advanced-projects |
Files
- ATTRIBUTION.mdAdded by AutoGineer895 B
- GENERATION.md136 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md3.6 KiB
- references/advanced-environments.md4.9 KiB
- references/advanced-projects.md7.4 KiB
- references/advanced-type-testing.md4.6 KiB
- references/advanced-vi.md5.8 KiB
- references/core-cli.md4.9 KiB
- references/core-config.md7.7 KiB
- references/core-describe.md3.5 KiB
- references/core-expect.md8.8 KiB
- references/core-hooks.md4.8 KiB
- references/core-test-api.md5.5 KiB
- references/features-benchmarking.md3.9 KiB
- references/features-concurrency.md4.7 KiB
- references/features-context.md5.3 KiB
- references/features-coverage.md5.0 KiB
- references/features-filtering.md4.8 KiB
- references/features-mocking.md9.8 KiB
- references/features-reporters.md4.0 KiB
- references/features-snapshots.md5.2 KiB
- references/features-test-tags.md3.6 KiB
references/features-mocking.md
name: mocking description: Mock functions, modules, timers, and dates with vi utilities
Mocking
Mock Functions
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')fn.mock.instances (and contexts) has one entry per call: this for plain calls, or the object new returned (undefined if the constructor threw). mockReturnValue/mockResolvedValue etc. throw when the mock is called with new; use mockImplementation for classes.
fn.mock.calls stores arguments by reference. If the argument is mutated after the call, assertions see the mutated value; clone at call time (e.g. mockImplementation(arg => calls.push(structuredClone(arg)))) when you need the pre-mutation state.
Spying on Objects
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()Since v4, vi.spyOn/vi.fn can mock constructors — provide a function or class implementation (an arrow function throws "not a constructor"):
const Spy = vi.spyOn(cart, 'Apples').mockImplementation(class {
getApples() { return 0 }
})
const instance = new Spy()Conditional Mocking with vi.when (v5)
Define per-argument behaviors without writing if/switch in mockImplementation:
vi.when(db.findById)
.calledWith(1)
.thenResolve({ id: 1, name: 'Ella' })
.calledWith(2)
.thenResolve({ id: 2, name: 'Gracie' })
// Actions: thenReturn / thenThrow / thenResolve / thenReject (+ *Once variants)
// `calledWith` supports asymmetric matchers
vi.when(sendEmail).calledWith(expect.stringContaining('@')).thenReturn({ ok: true })Behaviors match first-in-first-out (register specific before broad); stacked actions on one behavior consume last-in-first-out, with
{ times }to limit.Handle unmatched calls with
{ onUnmatched: 'throw' | fn }(default falls through to the original implementation).Assert all behaviors ran with
expect(w).toHaveBeenExhausted().
Auto-Cleanup with using
In runtimes with Explicit Resource Management (Node 24+, TS 5.2+), declare spies/mocks with using to auto-restore when the block exits — works with vi.spyOn, vi.fn, vi.doMock, and vi.when:
it('mocks console only here', () => {
using spy = vi.spyOn(console, 'log').mockImplementation(() => {})
debug('message')
expect(spy).toHaveBeenCalled()
} ) // console.log restored automatically — no afterEachModule Mocking
vi.mock, vi.unmock, and vi.hoisted are hoisted to the top of the file. In v5 calling them inside a function, block, or describe/test callback throws (it only warned before) — keep them at the module top level. Use vi.doMock/vi.doUnmock for non-hoisted, in-scope mocking.
// 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' })
})Typed Mocks with import()
Pass import('./db.js') instead of a string. TypeScript infers the module type, so the factory's return value is type-checked and importOriginal is typed. IDE renames/moves also update the path.
vi.mock(import('./db.js'), () => ({
getUser: vi.fn(),
}))Partial Mock
vi.mock(import('./utils'), async (importOriginal) => {
const actual = await importOriginal()
return {
...actual,
specificFunction: vi.fn(),
}
})Auto-mock with Spy
// 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// Just call vi.mock with no factory
vi.mock('axios')
vi.mock('./api/client')Dynamic Mocking (vi.doMock)
Not hoisted - use for dynamic imports:
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
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 timerAsync Timer Methods
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
vi.setSystemTime(new Date('2024-01-01'))
expect(new Date().getFullYear()).toBe(2024)
vi.useRealTimers() // Restorev5 fake timers (and vi.setSystemTime used without them) also mock Temporal when it's on the global object, not just Date:
vi.setSystemTime(0)
Temporal.Now.instant().epochMilliseconds // 0
// keep Temporal native:
vi.useFakeTimers({ toNotFake: ['Temporal'] })Auto-advancing and Tick Modes
While fake timers are on, vi.waitFor/vi.waitUntil advance timers by interval before each check, and expect.poll advances by interval after each failed attempt, so they don't hang. vi.setTimerTickMode('interval' | 'nextTimerAsync') makes timers advance by themselves: interval steps fake time on a real-time interval (for state with a limited lifetime), nextTimerAsync fires the next pending timer after each macrotask (for code that eventually settles).
vi.setSystemTime and the now option of vi.useFakeTimers also accept Temporal.Instant/Temporal.ZonedDateTime. Set fakeTimers.fakeNodeBuiltins: true to also fake node:timers and node:timers/promises.
Mock Globals
vi.stubGlobal('fetch', vi.fn(() =>
Promise.resolve({ json: () => ({ data: 'mock' }) })
))
// Restore
vi.unstubAllGlobals()Mock Environment Variables
vi.stubEnv('API_KEY', 'test-key')
expect(import.meta.env.API_KEY).toBe('test-key')
// Restore
vi.unstubAllEnvs()Clearing Mocks
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
// vitest.config.ts
defineConfig({
test: {
clearMocks: true, // Clear call history before each test — v5 DEFAULT
mockReset: true, // Reset before each test
restoreMocks: true, // Restore after each test
unstubEnvs: true, // Restore env vars
unstubGlobals: true, // Restore globals
},
})v5:
clearMocksdefaults totrue, so mock call history no longer leaks between tests. Mocks set up outside the test body (setup files, module top level,beforeAll) are most affected — their recorded calls are cleared before the asserting test runs. SetclearMocks: falseto restore the old behavior.
Hoisted Variables for Mocks
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')
})v5 Behavior Changes
Class mocks keep prototype methods.
vi.fn(Dog),vi.spyOn(obj, 'Dog'), and.mockImplementation(class …)now chain the mock'sprototypeto the implementation's, so instance methods work andinstanceof Dogpasses.mockResetreverts the chain.Automocked modules stay automocked in the browser — their exports return
undefinedunless you pass{ spy: true }or a factory.
v4 Behavior Changes
vi.fn().getMockName()returns'vi.fn()'(was'spy'); snapshots show[MockFunction]instead of[MockFunction spy].vi.restoreAllMocks(andrestoreMocks: true) now only restorevi.spyOnspies; automocks are unaffected..mockRestorestill resets a mock's implementation/state.vi.fn().mock.invocationCallOrderstarts at1(Jest parity).Automocked getters return
undefinedby default; automocked methods can't be restored.
Key Points
Prefer
vi.mockfor module mocking (hoisted - called before imports)Use
vi.doMockfor dynamic, non-hoisted mockingUse
vi.whenfor argument-specific behaviors;usingfor scoped auto-restoreUse
{ spy: true }to keep implementation but track callsvi.hoistedlets you reference variables in mock factoriesPrefer
vi.mock(import('./mod'), factory)over a string path for type-checked factories
<!-- Source references: - https://vitest.dev/guide/mocking.html - https://vitest.dev/guide/learn/mock-functions - https://vitest.dev/api/vi.html - https://vitest.dev/guide/recipes/conditional-mocking - https://vitest.dev/guide/recipes/explicit-resources -->
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Node.js
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Anthony Fu
- Publisher
- Anthony Fu (Community)
- Source
- antfu/skills · skills/vitest · commit f63e9ec
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Anthony Fu or Anthony Fu.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download vitest.zip