Vitest
02 / 02

Vitest Advanced: Mocking, Timers & Test Isolation

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