Vitest Cheatsheet

Spies

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.spyOn() — Spy on Existing Methods

import { vi, expect } from 'vitest'

const obj = {
  greet(name: string) {
    return `Hello, ${name}!`
  },
}

const spy = vi.spyOn(obj, 'greet')

obj.greet('Alice')

expect(spy).toHaveBeenCalledWith('Alice')
expect(spy).toHaveBeenCalledTimes(1)

spy.mockRestore() // restore original

Spy vs Mock

vi.fn()vi.spyOn()
Creates from scratchYesNo
Wraps existing methodNoYes
Calls through by defaultNo (returns undefined)Yes (original runs)
Supports .mockRestore()No-opYes — restores original

Spying Without Calling Through

const spy = vi.spyOn(obj, 'greet').mockReturnValue('Mocked!')
obj.greet('Bob') // returns 'Mocked!', original NOT called

Spying on Module Exports

import * as mathModule from './math'

const spy = vi.spyOn(mathModule, 'add')
spy.mockImplementation((a, b) => a * b) // spy replaces add with multiply

mathModule.add(2, 3) // returns 6, not 5
expect(spy).toHaveBeenCalledWith(2, 3)

spy.mockRestore()

Requires the module to export as a mutable namespace (works with * as ... imports in ESM).

Spying on Class Methods

class Logger {
  log(msg: string) {
    console.log(msg)
  }
}

const logger = new Logger()
const spy = vi.spyOn(logger, 'log')

logger.log('test')

expect(spy).toHaveBeenCalledWith('test')
spy.mockRestore()

Static methods:

const spy = vi.spyOn(Logger, 'create')

Spying on console

it('logs a warning', () => {
  const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
  
  myFunction() // internally calls console.warn(...)
  
  expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('deprecated'))
  consoleSpy.mockRestore()
})

Spying on Getters and Setters

const obj = { _value: 0 }
Object.defineProperty(obj, 'value', {
  get() { return this._value },
  set(v) { this._value = v },
  configurable: true,
})

const getSpy = vi.spyOn(obj, 'value', 'get').mockReturnValue(42)
expect(obj.value).toBe(42)

getSpy.mockRestore()

Checking Spy State

const spy = vi.spyOn(obj, 'method')
obj.method('a', 'b')
obj.method('c')

spy.mock.calls           // [['a', 'b'], ['c']]
spy.mock.results         // [{ type: 'return', value: ... }, ...]
spy.mock.lastCall        // ['c']
spy.mock.instances       // [obj, obj]

One-Time Override then Pass-Through

const spy = vi.spyOn(api, 'fetchData')
  .mockResolvedValueOnce({ data: 'cached' }) // first call: fake
  // second call: real implementation runs

await api.fetchData() // { data: 'cached' }
await api.fetchData() // real network call

Tracking Return Values

const spy = vi.spyOn(obj, 'compute')
obj.compute(5)
obj.compute(10)

spy.mock.results
// [
//   { type: 'return', value: 25 },
//   { type: 'return', value: 100 },
// ]

When the spy throws:

// { type: 'throw', value: Error('...') }

Restoring All Spies

afterEach(() => {
  vi.restoreAllMocks()
})

Or in config:

export default defineConfig({
  test: { restoreMocks: true }
})

Spy on Async Methods

const spy = vi.spyOn(service, 'getData').mockResolvedValue({ result: 'ok' })

const data = await service.getData()
expect(data).toEqual({ result: 'ok' })
expect(spy).toHaveBeenCalledTimes(1)

Checking Not Called

const spy = vi.spyOn(obj, 'expensiveOp')
doSomethingCheap()
expect(spy).not.toHaveBeenCalled()