Vitest Advanced: Mocking, Timers & Test Isolation
Mocking Functions & Modules
import { vi, test, expect } from 'vitest'
// vi.fn() -- a trackable stand-in function
const mockCallback = vi.fn()
mockCallback('hello')
expect(mockCallback).toHaveBeenCalledWith('hello')
// vi.mock() replaces an entire module -- vi.mock() calls are
// HOISTED above imports, so vi.hoisted() is needed for values
// referenced inside the factory
vi.mock('./api', () => ({
fetchUser: vi.fn(() => Promise.resolve({ name: 'Alice' })),
}))
// vi.spyOn() wraps a REAL method, still calling through by default --
// distinct from vi.fn(), which has no pre-existing implementation
const spy = vi.spyOn(console, 'log')
doSomething()
expect(spy).toHaveBeenCalledWith('done')Partial Mocking with importActual
// Keeps every real export except formatDate, which gets mocked --
// avoids hand-reimplementing every unrelated export
vi.mock('./utils', async () => {
const actual = await vi.importActual('./utils')
return { ...actual, formatDate: vi.fn(() => 'mocked-date') }
})Fake Timers
test('debounced function fires after delay', () => {
vi.useFakeTimers()
const fn = vi.fn()
const debounced = debounce(fn, 500)
debounced()
// Simulates 500ms passing instantly -- no real wall-clock wait needed
vi.advanceTimersByTime(500)
expect(fn).toHaveBeenCalledOnce()
vi.useRealTimers()
})Mock Cleanup: clear vs. reset
beforeEach(() => {
// clearAllMocks: wipes call history, KEEPS configured return values
// resetAllMocks: wipes call history AND custom implementations too --
// stricter, preventing a previous test's mock config from leaking
vi.resetAllMocks()
})Per-File Environment Override
// @vitest-environment jsdom
// Keeps most tests fast in the default `node` environment, opting
// only DOM-dependent files into the heavier simulated-browser environment
import { render } from '@testing-library/react'Detecting Test-Order Dependencies
test: { sequence: { shuffle: true } } randomizes test execution order, surfacing hidden dependencies where a test only passes because an earlier test left behind shared state it implicitly relies on -- a bug a fixed, always-identical order would otherwise mask indefinitely.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free