Vitest Cheatsheet

Mocking

Use this Vitest reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

vi.fn() — Mock Functions

import { vi, expect } from 'vitest'

const mockFn = vi.fn()
mockFn('arg1', 'arg2')

expect(mockFn).toHaveBeenCalledWith('arg1', 'arg2')
expect(mockFn).toHaveBeenCalledTimes(1)

Return Value Control

MethodEffect
.mockReturnValue(val)Always returns val
.mockReturnValueOnce(val)Returns val on next call, then resets
.mockResolvedValue(val)Returns Promise.resolve(val)
.mockResolvedValueOnce(val)One-time resolved promise
.mockRejectedValue(err)Returns Promise.reject(err)
.mockRejectedValueOnce(err)One-time rejected promise
.mockImplementation(fn)Replace implementation
.mockImplementationOnce(fn)One-time implementation
.mockReturnThis()Returns this (chaining)
.mockName(name)Set display name for error messages
const fn = vi.fn()
  .mockReturnValueOnce('first')
  .mockReturnValueOnce('second')
  .mockReturnValue('default')

fn() // 'first'
fn() // 'second'
fn() // 'default'
fn() // 'default'
const asyncFn = vi.fn()
  .mockResolvedValueOnce({ id: 1 })
  .mockRejectedValueOnce(new Error('network'))

await asyncFn() // { id: 1 }
await asyncFn() // throws Error('network')

Mocking Modules with vi.mock

// Hoist vi.mock calls — they run before imports
import { describe, it, expect, vi } from 'vitest'
import { fetchUser } from './api'

vi.mock('./api', () => ({
  fetchUser: vi.fn().mockResolvedValue({ id: 1, name: 'Alice' }),
}))

it('uses mocked fetchUser', async () => {
  const user = await fetchUser(1)
  expect(user.name).toBe('Alice')
})

vi.mock is automatically hoisted above import statements. Do not put it inside describe or it blocks — it always applies module-wide.

Partial Module Mocking

vi.mock('./utils', async (importOriginal) => {
  const actual = await importOriginal<typeof import('./utils')>()
  return {
    ...actual,
    formatDate: vi.fn().mockReturnValue('2024-01-01'),
    // everything else stays real
  }
})

vi.mocked() — Type-Safe Access to Mock

import { vi, expect } from 'vitest'
import { fetchUser } from './api'

vi.mock('./api')

it('is typed', async () => {
  vi.mocked(fetchUser).mockResolvedValue({ id: 2, name: 'Bob' })
  const user = await fetchUser(2)
  expect(user.name).toBe('Bob')
})

Auto-Mocking

vi.mock('./heavyModule') // all exports become vi.fn()

Vitest auto-generates mocks for every export. Functions become vi.fn(), classes have mocked constructors and methods.

vi.doMock / vi.doUnmock (Dynamic, Not Hoisted)

it('dynamic mock', async () => {
  vi.doMock('./config', () => ({ apiUrl: 'http://test' }))
  const { apiUrl } = await import('./config')
  expect(apiUrl).toBe('http://test')
  vi.doUnmock('./config')
})

Use vi.doMock when you need conditional or test-scoped mocking (it is NOT hoisted).

Mocking Default Exports

vi.mock('./logger', () => ({
  default: vi.fn(),
}))

import logger from './logger'
logger('msg')
expect(logger).toHaveBeenCalledWith('msg')

Mocking Classes

vi.mock('./Database', () => {
  return {
    Database: vi.fn().mockImplementation(() => ({
      connect: vi.fn().mockResolvedValue(true),
      query: vi.fn().mockResolvedValue([]),
      close: vi.fn(),
    })),
  }
})

import { Database } from './Database'
const db = new Database()
await db.connect()
expect(db.connect).toHaveBeenCalled()

Mocking Timers

import { vi, it, expect } from 'vitest'

it('advances timers', () => {
  vi.useFakeTimers()

  const fn = vi.fn()
  setTimeout(fn, 1000)

  expect(fn).not.toHaveBeenCalled()
  vi.advanceTimersByTime(1000)
  expect(fn).toHaveBeenCalledTimes(1)

  vi.useRealTimers()
})
Timer MethodEffect
vi.useFakeTimers()Replace global timers with fakes
vi.useRealTimers()Restore real timers
vi.advanceTimersByTime(ms)Advance clock by ms
vi.advanceTimersByTimeAsync(ms)Async version (runs microtasks)
vi.runAllTimers()Run all pending timers
vi.runAllTimersAsync()Async version
vi.runAllTicks()Run all microtasks (process.nextTick)
vi.clearAllTimers()Clear all pending timers
vi.setSystemTime(date)Set Date.now() to a specific time
vi.getRealSystemTime()Get the real wall-clock time

Mocking Date

vi.useFakeTimers()
vi.setSystemTime(new Date('2024-01-15'))

expect(new Date().toISOString()).toMatch('2024-01-15')

vi.useRealTimers()

Mocking Environment Variables

it('reads env var', () => {
  vi.stubEnv('API_URL', 'http://localhost:3000')
  expect(process.env.API_URL).toBe('http://localhost:3000')
  vi.unstubAllEnvs()
})

Mocking Globals

vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
  json: () => Promise.resolve({ data: 42 }),
  ok: true,
}))

// restore
vi.unstubAllGlobals()

Clearing, Resetting, Restoring Mocks

MethodEffect
mockFn.mockClear()Clear call history (.mock.calls, .mock.results)
mockFn.mockReset()Clear + remove all implementations
mockFn.mockRestore()Clear + restore original implementation (spies only)
vi.clearAllMocks()mockClear on all mocks
vi.resetAllMocks()mockReset on all mocks
vi.restoreAllMocks()mockRestore on all mocks

In config, auto-apply between tests:

export default defineConfig({
  test: {
    clearMocks: true,    // clear after each test
    mockReset: false,    // (Jest's name is resetMocks; Vitest's is mockReset)
    restoreMocks: false,
  },
})

Inspecting Mock Calls

const fn = vi.fn()
fn(1, 2)
fn(3, 4)

fn.mock.calls        // [[1, 2], [3, 4]]
fn.mock.calls[0]     // [1, 2]
fn.mock.results      // [{ type: 'return', value: undefined }, ...]
fn.mock.instances    // `this` for each call
fn.mock.lastCall     // [3, 4]