# Vitest API Reference


Test API Reference
The following types are used in the type signatures below


type Awaitable<T> = T | PromiseLike<T>
type TestFunction = () => Awaitable<void>

interface TestOptions {
  /**
   * Will fail the test if it takes too long to execute
   */
  timeout?: number
  /**
   * Will retry the test specific number of times if it fails
   *
   * @default 0
   */
  retry?: number
  /**
   * Will repeat the same test several times even if it fails each time
   * If you have "retry" option and it fails, it will use every retry in each cycle
   * Useful for debugging random failings
   *
   * @default 0
   */
  repeats?: number
}
When a test function returns a promise, the runner will wait until it is resolved to collect async expectations. If the promise is rejected, the test will fail.

TIP

In Jest, TestFunction can also be of type (done: DoneCallback) => void. If this form is used, the test will not be concluded until done is called. You can achieve the same using an async function, see the Migration guide Done Callback section.

You can define options by chaining properties on a function:


import { test } from 'vitest'

test.skip('skipped test', () => {
  // some logic that fails right now
})

test.concurrent.skip('skipped concurrent test', () => {
  // some logic that fails right now
})
But you can also provide an object as a second argument instead:


import { test } from 'vitest'

test('skipped test', { skip: true }, () => {
  // some logic that fails right now
})

test('skipped concurrent test', { skip: true, concurrent: true }, () => {
  // some logic that fails right now
})
They both work in exactly the same way. To use either one is purely a stylistic choice.

Note that if you are providing timeout as the last argument, you cannot use options anymore:


import { test } from 'vitest'

// ✅ this works
test.skip('heavy test', () => {
  // ...
}, 10_000)

// ❌ this doesn't work
test('heavy test', { skip: true }, () => {
  // ...
}, 10_000)
However, you can provide a timeout inside the object:


import { test } from 'vitest'

// ✅ this works
test('heavy test', { skip: true, timeout: 10_000 }, () => {
  // ...
})
test
Alias: it
test defines a set of related expectations. It receives the test name and a function that holds the expectations to test.

Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 5 seconds, and can be configured globally with testTimeout


import { expect, test } from 'vitest'

test('should work as expected', () => {
  expect(Math.sqrt(4)).toBe(2)
})
test.extend
Alias: it.extend
Use test.extend to extend the test context with custom fixtures. This will return a new test and it's also extendable, so you can compose more fixtures or override existing ones by extending it as you need. See Extend Test Context for more information.


import { expect, test } from 'vitest'

const todos = []
const archive = []

const myTest = test.extend({
  todos: async ({ task }, use) => {
    todos.push(1, 2, 3)
    await use(todos)
    todos.length = 0
  },
  archive
})

myTest('add item', ({ todos }) => {
  expect(todos.length).toBe(3)

  todos.push(4)
  expect(todos.length).toBe(4)
})
test.skip
Alias: it.skip
If you want to skip running certain tests, but you don't want to delete the code due to any reason, you can use test.skip to avoid running them.


import { assert, test } from 'vitest'

test.skip('skipped test', () => {
  // Test skipped, no error
  assert.equal(Math.sqrt(4), 3)
})
You can also skip test by calling skip on its context dynamically:


import { assert, test } from 'vitest'

test('skipped test', (context) => {
  context.skip()
  // Test skipped, no error
  assert.equal(Math.sqrt(4), 3)
})
Since Vitest 3.1, if the condition is unknown, you can provide it to the skip method as the first arguments:


import { assert, test } from 'vitest'

test('skipped test', (context) => {
  context.skip(Math.random() < 0.5, 'optional message')
  // Test skipped, no error
  assert.equal(Math.sqrt(4), 3)
})
test.skipIf
Alias: it.skipIf
In some cases you might run tests multiple times with different environments, and some of the tests might be environment-specific. Instead of wrapping the test code with if, you can use test.skipIf to skip the test whenever the condition is truthy.


import { assert, test } from 'vitest'

const isDev = process.env.NODE_ENV === 'development'

test.skipIf(isDev)('prod only test', () => {
  // this test only runs in production
})
WARNING

You cannot use this syntax when using Vitest as type checker.

test.runIf
Alias: it.runIf
Opposite of test.skipIf.


import { assert, test } from 'vitest'

const isDev = process.env.NODE_ENV === 'development'

test.runIf(isDev)('dev only test', () => {
  // this test only runs in development
})
WARNING

You cannot use this syntax when using Vitest as type checker.

test.only
Alias: it.only
Use test.only to only run certain tests in a given suite. This is useful when debugging.

Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 5 seconds, and can be configured globally with testTimeout.


import { assert, test } from 'vitest'

test.only('test', () => {
  // Only this test (and others marked with only) are run
  assert.equal(Math.sqrt(4), 2)
})
Sometimes it is very useful to run only tests in a certain file, ignoring all other tests from the whole test suite, which pollute the output.

In order to do that run vitest with specific file containing the tests in question.


# vitest interesting.test.ts
test.concurrent
Alias: it.concurrent
test.concurrent marks consecutive tests to be run in parallel. It receives the test name, an async function with the tests to collect, and an optional timeout (in milliseconds).


import { describe, test } from 'vitest'

// The two tests marked with concurrent will be run in parallel
describe('suite', () => {
  test('serial test', async () => { /* ... */ })
  test.concurrent('concurrent test 1', async () => { /* ... */ })
  test.concurrent('concurrent test 2', async () => { /* ... */ })
})
test.skip, test.only, and test.todo works with concurrent tests. All the following combinations are valid:


test.concurrent(/* ... */)
test.skip.concurrent(/* ... */) // or test.concurrent.skip(/* ... */)
test.only.concurrent(/* ... */) // or test.concurrent.only(/* ... */)
test.todo.concurrent(/* ... */) // or test.concurrent.todo(/* ... */)
When running concurrent tests, Snapshots and Assertions must use expect from the local Test Context to ensure the right test is detected.


test.concurrent('test 1', async ({ expect }) => {
  expect(foo).toMatchSnapshot()
})
test.concurrent('test 2', async ({ expect }) => {
  expect(foo).toMatchSnapshot()
})
WARNING

You cannot use this syntax when using Vitest as type checker.

test.sequential
Alias: it.sequential
test.sequential marks a test as sequential. This is useful if you want to run tests in sequence within describe.concurrent or with the --sequence.concurrent command option.


import { describe, test } from 'vitest'

// with config option { sequence: { concurrent: true } }
test('concurrent test 1', async () => { /* ... */ })
test('concurrent test 2', async () => { /* ... */ })

test.sequential('sequential test 1', async () => { /* ... */ })
test.sequential('sequential test 2', async () => { /* ... */ })

// within concurrent suite
describe.concurrent('suite', () => {
  test('concurrent test 1', async () => { /* ... */ })
  test('concurrent test 2', async () => { /* ... */ })

  test.sequential('sequential test 1', async () => { /* ... */ })
  test.sequential('sequential test 2', async () => { /* ... */ })
})
test.todo
Alias: it.todo
Use test.todo to stub tests to be implemented later. An entry will be shown in the report for the tests so you know how many tests you still need to implement.


// An entry will be shown in the report for this test
test.todo('unimplemented test')
test.fails
Alias: it.fails
Use test.fails to indicate that an assertion will fail explicitly.


import { expect, test } from 'vitest'

function myAsyncFunc() {
  return new Promise(resolve => resolve(1))
}
test.fails('fail test', async () => {
  await expect(myAsyncFunc()).rejects.toBe(1)
})
WARNING

You cannot use this syntax when using Vitest as type checker.

test.each
Alias: it.each
TIP

While test.each is provided for Jest compatibility, Vitest also has test.for with an additional feature to integrate TestContext.

Use test.each when you need to run the same test with different variables. You can inject parameters with printf formatting in the test name in the order of the test function parameters.

%s: string
%d: number
%i: integer
%f: floating point value
%j: json
%o: object
%#: 0-based index of the test case
%$: 1-based index of the test case
%%: single percent sign ('%')

import { expect, test } from 'vitest'

test.each([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add(%i, %i) -> %i', (a, b, expected) => {
  expect(a + b).toBe(expected)
})

// this will return
// ✓ add(1, 1) -> 2
// ✓ add(1, 2) -> 3
// ✓ add(2, 1) -> 3
You can also access object properties and array elements with $ prefix:


test.each([
  { a: 1, b: 1, expected: 2 },
  { a: 1, b: 2, expected: 3 },
  { a: 2, b: 1, expected: 3 },
])('add($a, $b) -> $expected', ({ a, b, expected }) => {
  expect(a + b).toBe(expected)
})

// this will return
// ✓ add(1, 1) -> 2
// ✓ add(1, 2) -> 3
// ✓ add(2, 1) -> 3

test.each([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add($0, $1) -> $2', (a, b, expected) => {
  expect(a + b).toBe(expected)
})

// this will return
// ✓ add(1, 1) -> 2
// ✓ add(1, 2) -> 3
// ✓ add(2, 1) -> 3
You can also access Object attributes with ., if you are using objects as arguments:


test.each`
a               | b      | expected
${{ val: 1 }}   | ${'b'} | ${'1b'}
${{ val: 2 }}   | ${'b'} | ${'2b'}
${{ val: 3 }}   | ${'b'} | ${'3b'}
`('add($a.val, $b) -> $expected', ({ a, b, expected }) => {
  expect(a.val + b).toBe(expected)
})

// this will return
// ✓ add(1, b) -> 1b
// ✓ add(2, b) -> 2b
// ✓ add(3, b) -> 3b
Starting from Vitest 0.25.3, you can also use template string table.

First row should be column names, separated by |;
One or more subsequent rows of data supplied as template literal expressions using ${value} syntax.

import { expect, test } from 'vitest'

test.each`
  a               | b      | expected
  ${1}            | ${1}   | ${2}
  ${'a'}          | ${'b'} | ${'ab'}
  ${[]}           | ${'b'} | ${'b'}
  ${{}}           | ${'b'} | ${'[object Object]b'}
  ${{ asd: 1 }}   | ${'b'} | ${'[object Object]b'}
`('returns $expected when $a is added $b', ({ a, b, expected }) => {
  expect(a + b).toBe(expected)
})
TIP

Vitest processes $values with Chai format method. If the value is too truncated, you can increase chaiConfig.truncateThreshold in your config file.

WARNING

You cannot use this syntax when using Vitest as type checker.

test.for
Alias: it.for
Alternative to test.each to provide TestContext.

The difference from test.each lies in how arrays are provided in the arguments. Non-array arguments to test.for (including template string usage) work exactly the same as for test.each.


// `each` spreads arrays
test.each([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add(%i, %i) -> %i', (a, b, expected) => { 
  expect(a + b).toBe(expected)
})

// `for` doesn't spread arrays (notice the square brackets around the arguments)
test.for([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add(%i, %i) -> %i', ([a, b, expected]) => { 
  expect(a + b).toBe(expected)
})
The 2nd argument is TestContext and can be used for concurrent snapshots, for example:


test.concurrent.for([
  [1, 1],
  [1, 2],
  [2, 1],
])('add(%i, %i)', ([a, b], { expect }) => {
  expect(a + b).matchSnapshot()
})
bench
Type: (name: string | Function, fn: BenchFunction, options?: BenchOptions) => void
bench defines a benchmark. In Vitest terms, benchmark is a function that defines a series of operations. Vitest runs this function multiple times to display different performance results.

Vitest uses the tinybench library under the hood, inheriting all its options that can be used as a third argument.


import { bench } from 'vitest'

bench('normal sorting', () => {
  const x = [1, 5, 4, 2, 3]
  x.sort((a, b) => {
    return a - b
  })
}, { time: 1000 })

export interface Options {
  /**
   * time needed for running a benchmark task (milliseconds)
   * @default 500
   */
  time?: number

  /**
   * number of times that a task should run if even the time option is finished
   * @default 10
   */
  iterations?: number

  /**
   * function to get the current timestamp in milliseconds
   */
  now?: () => number

  /**
   * An AbortSignal for aborting the benchmark
   */
  signal?: AbortSignal

  /**
   * Throw if a task fails (events will not work if true)
   */
  throws?: boolean

  /**
   * warmup time (milliseconds)
   * @default 100ms
   */
  warmupTime?: number

  /**
   * warmup iterations
   * @default 5
   */
  warmupIterations?: number

  /**
   * setup function to run before each benchmark task (cycle)
   */
  setup?: Hook

  /**
   * teardown function to run after each benchmark task (cycle)
   */
  teardown?: Hook
}
After the test case is run, the output structure information is as follows:


  name                      hz     min     max    mean     p75     p99    p995    p999     rme  samples
· normal sorting  6,526,368.12  0.0001  0.3638  0.0002  0.0002  0.0002  0.0002  0.0004  ±1.41%   652638

export interface TaskResult {
  /*
   * the last error that was thrown while running the task
   */
  error?: unknown

  /**
   * The amount of time in milliseconds to run the benchmark task (cycle).
   */
  totalTime: number

  /**
   * the minimum value in the samples
   */
  min: number
  /**
   * the maximum value in the samples
   */
  max: number

  /**
   * the number of operations per second
   */
  hz: number

  /**
   * how long each operation takes (ms)
   */
  period: number

  /**
   * task samples of each task iteration time (ms)
   */
  samples: number[]

  /**
   * samples mean/average (estimate of the population mean)
   */
  mean: number

  /**
   * samples variance (estimate of the population variance)
   */
  variance: number

  /**
   * samples standard deviation (estimate of the population standard deviation)
   */
  sd: number

  /**
   * standard error of the mean (a.k.a. the standard deviation of the sampling distribution of the sample mean)
   */
  sem: number

  /**
   * degrees of freedom
   */
  df: number

  /**
   * critical value of the samples
   */
  critical: number

  /**
   * margin of error
   */
  moe: number

  /**
   * relative margin of error
   */
  rme: number

  /**
   * median absolute deviation
   */
  mad: number

  /**
   * p50/median percentile
   */
  p50: number

  /**
   * p75 percentile
   */
  p75: number

  /**
   * p99 percentile
   */
  p99: number

  /**
   * p995 percentile
   */
  p995: number

  /**
   * p999 percentile
   */
  p999: number
}
bench.skip
Type: (name: string | Function, fn: BenchFunction, options?: BenchOptions) => void
You can use bench.skip syntax to skip running certain benchmarks.


import { bench } from 'vitest'

bench.skip('normal sorting', () => {
  const x = [1, 5, 4, 2, 3]
  x.sort((a, b) => {
    return a - b
  })
})
bench.only
Type: (name: string | Function, fn: BenchFunction, options?: BenchOptions) => void
Use bench.only to only run certain benchmarks in a given suite. This is useful when debugging.


import { bench } from 'vitest'

bench.only('normal sorting', () => {
  const x = [1, 5, 4, 2, 3]
  x.sort((a, b) => {
    return a - b
  })
})
bench.todo
Type: (name: string | Function) => void
Use bench.todo to stub benchmarks to be implemented later.


import { bench } from 'vitest'

bench.todo('unimplemented test')
describe
When you use test or bench in the top level of file, they are collected as part of the implicit suite for it. Using describe you can define a new suite in the current context, as a set of related tests or benchmarks and other nested suites. A suite lets you organize your tests and benchmarks so reports are more clear.


// basic.spec.ts
// organizing tests

import { describe, expect, test } from 'vitest'

const person = {
  isActive: true,
  age: 32,
}

describe('person', () => {
  test('person is defined', () => {
    expect(person).toBeDefined()
  })

  test('is active', () => {
    expect(person.isActive).toBeTruthy()
  })

  test('age limit', () => {
    expect(person.age).toBeLessThanOrEqual(32)
  })
})

// basic.bench.ts
// organizing benchmarks

import { bench, describe } from 'vitest'

describe('sort', () => {
  bench('normal', () => {
    const x = [1, 5, 4, 2, 3]
    x.sort((a, b) => {
      return a - b
    })
  })

  bench('reverse', () => {
    const x = [1, 5, 4, 2, 3]
    x.reverse().sort((a, b) => {
      return a - b
    })
  })
})
You can also nest describe blocks if you have a hierarchy of tests or benchmarks:


import { describe, expect, test } from 'vitest'

function numberToCurrency(value: number | string) {
  if (typeof value !== 'number') {
    throw new TypeError('Value must be a number')
  }

  return value.toFixed(2).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
}

describe('numberToCurrency', () => {
  describe('given an invalid number', () => {
    test('composed of non-numbers to throw error', () => {
      expect(() => numberToCurrency('abc')).toThrowError()
    })
  })

  describe('given a valid number', () => {
    test('returns the correct currency format', () => {
      expect(numberToCurrency(10000)).toBe('10,000.00')
    })
  })
})
describe.skip
Alias: suite.skip
Use describe.skip in a suite to avoid running a particular describe block.


import { assert, describe, test } from 'vitest'

describe.skip('skipped suite', () => {
  test('sqrt', () => {
    // Suite skipped, no error
    assert.equal(Math.sqrt(4), 3)
  })
})
describe.skipIf
Alias: suite.skipIf
In some cases, you might run suites multiple times with different environments, and some of the suites might be environment-specific. Instead of wrapping the suite with if, you can use describe.skipIf to skip the suite whenever the condition is truthy.


import { describe, test } from 'vitest'

const isDev = process.env.NODE_ENV === 'development'

describe.skipIf(isDev)('prod only test suite', () => {
  // this test suite only runs in production
})
WARNING

You cannot use this syntax when using Vitest as type checker.

describe.runIf
Alias: suite.runIf
Opposite of describe.skipIf.


import { assert, describe, test } from 'vitest'

const isDev = process.env.NODE_ENV === 'development'

describe.runIf(isDev)('dev only test suite', () => {
  // this test suite only runs in development
})
WARNING

You cannot use this syntax when using Vitest as type checker.

describe.only
Type: (name: string | Function, fn: TestFunction, options?: number | TestOptions) => void
Use describe.only to only run certain suites


import { assert, describe, test } from 'vitest'

// Only this suite (and others marked with only) are run
describe.only('suite', () => {
  test('sqrt', () => {
    assert.equal(Math.sqrt(4), 3)
  })
})

describe('other suite', () => {
  // ... will be skipped
})
Sometimes it is very useful to run only tests in a certain file, ignoring all other tests from the whole test suite, which pollute the output.

In order to do that run vitest with specific file containing the tests in question.


# vitest interesting.test.ts
describe.concurrent
Alias: suite.concurrent
describe.concurrent runs all inner suites and tests in parallel


import { describe, test } from 'vitest'

// All suites and tests within this suite will be run in parallel
describe.concurrent('suite', () => {
  test('concurrent test 1', async () => { /* ... */ })
  describe('concurrent suite 2', async () => {
    test('concurrent test inner 1', async () => { /* ... */ })
    test('concurrent test inner 2', async () => { /* ... */ })
  })
  test.concurrent('concurrent test 3', async () => { /* ... */ })
})
.skip, .only, and .todo works with concurrent suites. All the following combinations are valid:


describe.concurrent(/* ... */)
describe.skip.concurrent(/* ... */) // or describe.concurrent.skip(/* ... */)
describe.only.concurrent(/* ... */) // or describe.concurrent.only(/* ... */)
describe.todo.concurrent(/* ... */) // or describe.concurrent.todo(/* ... */)
When running concurrent tests, Snapshots and Assertions must use expect from the local Test Context to ensure the right test is detected.


describe.concurrent('suite', () => {
  test('concurrent test 1', async ({ expect }) => {
    expect(foo).toMatchSnapshot()
  })
  test('concurrent test 2', async ({ expect }) => {
    expect(foo).toMatchSnapshot()
  })
})
WARNING

You cannot use this syntax when using Vitest as type checker.

describe.sequential
Alias: suite.sequential
describe.sequential in a suite marks every test as sequential. This is useful if you want to run tests in sequence within describe.concurrent or with the --sequence.concurrent command option.


import { describe, test } from 'vitest'

describe.concurrent('suite', () => {
  test('concurrent test 1', async () => { /* ... */ })
  test('concurrent test 2', async () => { /* ... */ })

  describe.sequential('', () => {
    test('sequential test 1', async () => { /* ... */ })
    test('sequential test 2', async () => { /* ... */ })
  })
})
describe.shuffle
Alias: suite.shuffle
Vitest provides a way to run all tests in random order via CLI flag --sequence.shuffle or config option sequence.shuffle, but if you want to have only part of your test suite to run tests in random order, you can mark it with this flag.


import { describe, test } from 'vitest'

// or describe('suite', { shuffle: true }, ...)
describe.shuffle('suite', () => {
  test('random test 1', async () => { /* ... */ })
  test('random test 2', async () => { /* ... */ })
  test('random test 3', async () => { /* ... */ })

  // `shuffle` is inherited
  describe('still random', () => {
    test('random 4.1', async () => { /* ... */ })
    test('random 4.2', async () => { /* ... */ })
  })

  // disable shuffle inside
  describe('not random', { shuffle: false }, () => {
    test('in order 5.1', async () => { /* ... */ })
    test('in order 5.2', async () => { /* ... */ })
  })
})
// order depends on sequence.seed option in config (Date.now() by default)
.skip, .only, and .todo works with random suites.

WARNING

You cannot use this syntax when using Vitest as type checker.

describe.todo
Alias: suite.todo
Use describe.todo to stub suites to be implemented later. An entry will be shown in the report for the tests so you know how many tests you still need to implement.


// An entry will be shown in the report for this suite
describe.todo('unimplemented suite')
describe.each
Alias: suite.each
TIP

While describe.each is provided for Jest compatibility, Vitest also has describe.for which simplifies argument types and aligns with test.for.

Use describe.each if you have more than one test that depends on the same data.


import { describe, expect, test } from 'vitest'

describe.each([
  { a: 1, b: 1, expected: 2 },
  { a: 1, b: 2, expected: 3 },
  { a: 2, b: 1, expected: 3 },
])('describe object add($a, $b)', ({ a, b, expected }) => {
  test(`returns ${expected}`, () => {
    expect(a + b).toBe(expected)
  })

  test(`returned value not be greater than ${expected}`, () => {
    expect(a + b).not.toBeGreaterThan(expected)
  })

  test(`returned value not be less than ${expected}`, () => {
    expect(a + b).not.toBeLessThan(expected)
  })
})
Starting from Vitest 0.25.3, you can also use template string table.

First row should be column names, separated by |;
One or more subsequent rows of data supplied as template literal expressions using ${value} syntax.

import { describe, expect, test } from 'vitest'

describe.each`
  a               | b      | expected
  ${1}            | ${1}   | ${2}
  ${'a'}          | ${'b'} | ${'ab'}
  ${[]}           | ${'b'} | ${'b'}
  ${{}}           | ${'b'} | ${'[object Object]b'}
  ${{ asd: 1 }}   | ${'b'} | ${'[object Object]b'}
`('describe template string add($a, $b)', ({ a, b, expected }) => {
  test(`returns ${expected}`, () => {
    expect(a + b).toBe(expected)
  })
})
WARNING

You cannot use this syntax when using Vitest as type checker.

describe.for
Alias: suite.for
The difference from describe.each is how array case is provided in the arguments. Other non array case (including template string usage) works exactly same.


// `each` spreads array case
describe.each([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add(%i, %i) -> %i', (a, b, expected) => { 
  test('test', () => {
    expect(a + b).toBe(expected)
  })
})

// `for` doesn't spread array case
describe.for([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add(%i, %i) -> %i', ([a, b, expected]) => { 
  test('test', () => {
    expect(a + b).toBe(expected)
  })
})
Setup and Teardown
These functions allow you to hook into the life cycle of tests to avoid repeating setup and teardown code. They apply to the current context: the file if they are used at the top-level or the current suite if they are inside a describe block. These hooks are not called, when you are running Vitest as a type checker.

beforeEach
Type: beforeEach(fn: () => Awaitable<void>, timeout?: number)
Register a callback to be called before each of the tests in the current context runs. If the function returns a promise, Vitest waits until the promise resolve before running the test.

Optionally, you can pass a timeout (in milliseconds) defining how long to wait before terminating. The default is 5 seconds.


import { beforeEach } from 'vitest'

beforeEach(async () => {
  // Clear mocks and add some testing data before each test run
  await stopMocking()
  await addUser({ name: 'John' })
})
Here, the beforeEach ensures that user is added for each test.

beforeEach also accepts an optional cleanup function (equivalent to afterEach).


import { beforeEach } from 'vitest'

beforeEach(async () => {
  // called once before each test run
  await prepareSomething()

  // clean up function, called once after each test run
  return async () => {
    await resetSomething()
  }
})
afterEach
Type: afterEach(fn: () => Awaitable<void>, timeout?: number)
Register a callback to be called after each one of the tests in the current context completes. If the function returns a promise, Vitest waits until the promise resolve before continuing.

Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 5 seconds.


import { afterEach } from 'vitest'

afterEach(async () => {
  await clearTestingData() // clear testing data after each test run
})
Here, the afterEach ensures that testing data is cleared after each test runs.

TIP

Vitest 1.3.0 added onTestFinished hook. You can call it during the test execution to cleanup any state after the test has finished running.

beforeAll
Type: beforeAll(fn: () => Awaitable<void>, timeout?: number)
Register a callback to be called once before starting to run all tests in the current context. If the function returns a promise, Vitest waits until the promise resolve before running tests.

Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 5 seconds.


import { beforeAll } from 'vitest'

beforeAll(async () => {
  await startMocking() // called once before all tests run
})
Here the beforeAll ensures that the mock data is set up before tests run.

beforeAll also accepts an optional cleanup function (equivalent to afterAll).


import { beforeAll } from 'vitest'

beforeAll(async () => {
  // called once before all tests run
  await startMocking()

  // clean up function, called once after all tests run
  return async () => {
    await stopMocking()
  }
})
afterAll
Type: afterAll(fn: () => Awaitable<void>, timeout?: number)
Register a callback to be called once after all tests have run in the current context. If the function returns a promise, Vitest waits until the promise resolve before continuing.

Optionally, you can provide a timeout (in milliseconds) for specifying how long to wait before terminating. The default is 5 seconds.


import { afterAll } from 'vitest'

afterAll(async () => {
  await stopMocking() // this method is called after all tests run
})
Here the afterAll ensures that stopMocking method is called after all tests run.

Test Hooks
Vitest provides a few hooks that you can call during the test execution to cleanup the state when the test has finished running.

WARNING

These hooks will throw an error if they are called outside of the test body.

onTestFinished
This hook is always called after the test has finished running. It is called after afterEach hooks since they can influence the test result. It receives an ExtendedContext object like beforeEach and afterEach.


import { onTestFinished, test } from 'vitest'

test('performs a query', () => {
  const db = connectDb()
  onTestFinished(() => db.close())
  db.query('SELECT * FROM users')
})
WARNING

If you are running tests concurrently, you should always use onTestFinished hook from the test context since Vitest doesn't track concurrent tests in global hooks:


import { test } from 'vitest'

test.concurrent('performs a query', ({ onTestFinished }) => {
  const db = connectDb()
  onTestFinished(() => db.close())
  db.query('SELECT * FROM users')
})
This hook is particularly useful when creating reusable logic:


// this can be in a separate file
function getTestDb() {
  const db = connectMockedDb()
  onTestFinished(() => db.close())
  return db
}

test('performs a user query', async () => {
  const db = getTestDb()
  expect(
    await db.query('SELECT * from users').perform()
  ).toEqual([])
})

test('performs an organization query', async () => {
  const db = getTestDb()
  expect(
    await db.query('SELECT * from organizations').perform()
  ).toEqual([])
})
TIP

This hook is always called in reverse order and is not affected by sequence.hooks option.

onTestFailed
This hook is called only after the test has failed. It is called after afterEach hooks since they can influence the test result. It receives an ExtendedContext object like beforeEach and afterEach. This hook is useful for debugging.


import { onTestFailed, test } from 'vitest'

test('performs a query', () => {
  const db = connectDb()
  onTestFailed(({ task }) => {
    console.log(task.result.errors)
  })
  db.query('SELECT * FROM users')
})
WARNING

If you are running tests concurrently, you should always use onTestFailed hook from the test context since Vitest doesn't track concurrent tests in global hooks:


import { test } from 'vitest'

test.concurrent('performs a query', ({ onTestFailed }) => {
  const db = connectDb()
  onTestFailed(({ task }) => {
    console.log(task.result.errors)
  })
  db.query('SELECT * FROM users')
})

# Vitest Mock Functions

Mock Functions
You can create a mock function to track its execution with vi.fn method. If you want to track a method on an already created object, you can use vi.spyOn method:


import { vi } from 'vitest'

const fn = vi.fn()
fn('hello world')
fn.mock.calls[0] === ['hello world']

const market = {
  getApples: () => 100
}

const getApplesSpy = vi.spyOn(market, 'getApples')
market.getApples()
getApplesSpy.mock.calls.length === 1
You should use mock assertions (e.g., toHaveBeenCalled) on expect to assert mock result. This API reference describes available properties and methods to manipulate mock behavior.

TIP

The custom function implementation in the types below is marked with a generic <T>.

getMockImplementation

function getMockImplementation(): T | undefined
Returns current mock implementation if there is one.

If the mock was created with vi.fn, it will use the provided method as the mock implementation.

If the mock was created with vi.spyOn, it will return undefined unless a custom implementation is provided.

getMockName

function getMockName(): string
Use it to return the name assigned to the mock with the .mockName(name) method. By default, it will return vi.fn().

mockClear

function mockClear(): MockInstance<T>
Clears all information about every call. After calling it, all properties on .mock will return to their initial state. This method does not reset implementations. It is useful for cleaning up mocks between different assertions.


const person = {
  greet: (name: string) => `Hello ${name}`,
}
const spy = vi.spyOn(person, 'greet').mockImplementation(() => 'mocked')
expect(person.greet('Alice')).toBe('mocked')
expect(spy.mock.calls).toEqual([['Alice']])

// clear call history but keep mock implementation
spy.mockClear()
expect(spy.mock.calls).toEqual([])
expect(person.greet('Bob')).toBe('mocked')
expect(spy.mock.calls).toEqual([['Bob']])
To automatically call this method before each test, enable the clearMocks setting in the configuration.

mockName

function mockName(name: string): MockInstance<T>
Sets the internal mock name. This is useful for identifying the mock when an assertion fails.

mockImplementation

function mockImplementation(fn: T): MockInstance<T>
Accepts a function to be used as the mock implementation. TypeScript expects the arguments and return type to match those of the original function.


const mockFn = vi.fn().mockImplementation((apples: number) => apples + 1)
// or: vi.fn(apples => apples + 1);

const NelliesBucket = mockFn(0)
const BobsBucket = mockFn(1)

NelliesBucket === 1 // true
BobsBucket === 2 // true

mockFn.mock.calls[0][0] === 0 // true
mockFn.mock.calls[1][0] === 1 // true
mockImplementationOnce

function mockImplementationOnce(fn: T): MockInstance<T>
Accepts a function to be used as the mock implementation. TypeScript expects the arguments and return type to match those of the original function. This method can be chained to produce different results for multiple function calls.


const myMockFn = vi
  .fn()
  .mockImplementationOnce(() => true) // 1st call
  .mockImplementationOnce(() => false) // 2nd call

myMockFn() // 1st call: true
myMockFn() // 2nd call: false
When the mocked function runs out of implementations, it will invoke the default implementation set with vi.fn(() => defaultValue) or .mockImplementation(() => defaultValue) if they were called:


const myMockFn = vi
  .fn(() => 'default')
  .mockImplementationOnce(() => 'first call')
  .mockImplementationOnce(() => 'second call')

// 'first call', 'second call', 'default', 'default'
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn())
withImplementation

function withImplementation(
  fn: T,
  cb: () => void
): MockInstance<T>
function withImplementation(
  fn: T,
  cb: () => Promise<void>
): Promise<MockInstance<T>>
Overrides the original mock implementation temporarily while the callback is being executed.


const myMockFn = vi.fn(() => 'original')

myMockFn.withImplementation(() => 'temp', () => {
  myMockFn() // 'temp'
})

myMockFn() // 'original'
Can be used with an asynchronous callback. The method has to be awaited to use the original implementation afterward.


test('async callback', () => {
  const myMockFn = vi.fn(() => 'original')

  // We await this call since the callback is async
  await myMockFn.withImplementation(
    () => 'temp',
    async () => {
      myMockFn() // 'temp'
    },
  )

  myMockFn() // 'original'
})
Note that this method takes precedence over the mockImplementationOnce.

mockRejectedValue

function mockRejectedValue(value: unknown): MockInstance<T>
Accepts an error that will be rejected when async function is called.


const asyncMock = vi.fn().mockRejectedValue(new Error('Async error'))

await asyncMock() // throws Error<'Async error'>
mockRejectedValueOnce

function mockRejectedValueOnce(value: unknown): MockInstance<T>
Accepts a value that will be rejected during the next function call. If chained, each consecutive call will reject the specified value.


const asyncMock = vi
  .fn()
  .mockResolvedValueOnce('first call')
  .mockRejectedValueOnce(new Error('Async error'))

await asyncMock() // 'first call'
await asyncMock() // throws Error<'Async error'>
mockReset

function mockReset(): MockInstance<T>
Does what mockClear does and resets inner implementation to the original function. This also resets all "once" implementations.

Note that resetting a mock from vi.fn() will set implementation to an empty function that returns undefined. resetting a mock from vi.fn(impl) will restore implementation to impl.

This is useful when you want to reset a mock to its original state.


const person = {
  greet: (name: string) => `Hello ${name}`,
}
const spy = vi.spyOn(person, 'greet').mockImplementation(() => 'mocked')
expect(person.greet('Alice')).toBe('mocked')
expect(spy.mock.calls).toEqual([['Alice']])

// clear call history and reset implementation, but method is still spied
spy.mockReset()
expect(spy.mock.calls).toEqual([])
expect(person.greet).toBe(spy)
expect(person.greet('Bob')).toBe('Hello Bob')
expect(spy.mock.calls).toEqual([['Bob']])
To automatically call this method before each test, enable the mockReset setting in the configuration.

mockRestore

function mockRestore(): MockInstance<T>
Does what mockReset does and restores original descriptors of spied-on objects.

Note that restoring a mock from vi.fn() will set implementation to an empty function that returns undefined. Restoring a mock from vi.fn(impl) will restore implementation to impl.


const person = {
  greet: (name: string) => `Hello ${name}`,
}
const spy = vi.spyOn(person, 'greet').mockImplementation(() => 'mocked')
expect(person.greet('Alice')).toBe('mocked')
expect(spy.mock.calls).toEqual([['Alice']])

// clear call history and restore spied object method
spy.mockRestore()
expect(spy.mock.calls).toEqual([])
expect(person.greet).not.toBe(spy)
expect(person.greet('Bob')).toBe('Hello Bob')
expect(spy.mock.calls).toEqual([])
To automatically call this method before each test, enable the restoreMocks setting in the configuration.

mockResolvedValue

function mockResolvedValue(value: Awaited<ReturnType<T>>): MockInstance<T>
Accepts a value that will be resolved when the async function is called. TypeScript will only accept values that match the return type of the original function.


const asyncMock = vi.fn().mockResolvedValue(42)

await asyncMock() // 42
mockResolvedValueOnce

function mockResolvedValueOnce(value: Awaited<ReturnType<T>>): MockInstance<T>
Accepts a value that will be resolved during the next function call. TypeScript will only accept values that match the return type of the original function. If chained, each consecutive call will resolve the specified value.


const asyncMock = vi
  .fn()
  .mockResolvedValue('default')
  .mockResolvedValueOnce('first call')
  .mockResolvedValueOnce('second call')

await asyncMock() // first call
await asyncMock() // second call
await asyncMock() // default
await asyncMock() // default
mockReturnThis

function mockReturnThis(): MockInstance<T>
Use this if you need to return the this context from the method without invoking the actual implementation. This is a shorthand for:


spy.mockImplementation(function () {
  return this
})
mockReturnValue

function mockReturnValue(value: ReturnType<T>): MockInstance<T>
Accepts a value that will be returned whenever the mock function is called. TypeScript will only accept values that match the return type of the original function.


const mock = vi.fn()
mock.mockReturnValue(42)
mock() // 42
mock.mockReturnValue(43)
mock() // 43
mockReturnValueOnce

function mockReturnValueOnce(value: ReturnType<T>): MockInstance<T>
Accepts a value that will be returned whenever the mock function is called. TypeScript will only accept values that match the return type of the original function.

When the mocked function runs out of implementations, it will invoke the default implementation set with vi.fn(() => defaultValue) or .mockImplementation(() => defaultValue) if they were called:


const myMockFn = vi
  .fn()
  .mockReturnValue('default')
  .mockReturnValueOnce('first call')
  .mockReturnValueOnce('second call')

// 'first call', 'second call', 'default', 'default'
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn())
mock.calls

const calls: Parameters<T>[]
This is an array containing all arguments for each call. One item of the array is the arguments of that call.


const fn = vi.fn()

fn('arg1', 'arg2')
fn('arg3')

fn.mock.calls === [
  ['arg1', 'arg2'], // first call
  ['arg3'], // second call
]
mock.lastCall

const lastCall: Parameters<T> | undefined
This contains the arguments of the last call. If mock wasn't called, it will return undefined.

mock.results

interface MockResultReturn<T> {
  type: 'return'
  /**
   * The value that was returned from the function.
   * If function returned a Promise, then this will be a resolved value.
   */
  value: T
}

interface MockResultIncomplete {
  type: 'incomplete'
  value: undefined
}

interface MockResultThrow {
  type: 'throw'
  /**
   * An error that was thrown during function execution.
   */
  value: any
}

type MockResult<T> =
  | MockResultReturn<T>
  | MockResultThrow
  | MockResultIncomplete

const results: MockResult<ReturnType<T>>[]
This is an array containing all values that were returned from the function. One item of the array is an object with properties type and value. Available types are:

'return' - function returned without throwing.
'throw' - function threw a value.
The value property contains the returned value or thrown error. If the function returned a Promise, then result will always be 'return' even if the promise was rejected.


const fn = vi.fn()
  .mockReturnValueOnce('result')
  .mockImplementationOnce(() => { throw new Error('thrown error') })

const result = fn() // returned 'result'

try {
  fn() // threw Error
}
catch {}

fn.mock.results === [
  // first result
  {
    type: 'return',
    value: 'result',
  },
  // last result
  {
    type: 'throw',
    value: Error,
  },
]
mock.settledResults

interface MockSettledResultFulfilled<T> {
  type: 'fulfilled'
  value: T
}

interface MockSettledResultRejected {
  type: 'rejected'
  value: any
}

export type MockSettledResult<T> =
  | MockSettledResultFulfilled<T>
  | MockSettledResultRejected

const settledResults: MockSettledResult<Awaited<ReturnType<T>>>[]
An array containing all values that were resolved or rejected from the function.

This array will be empty if the function was never resolved or rejected.


const fn = vi.fn().mockResolvedValueOnce('result')

const result = fn()

fn.mock.settledResults === []

await result

fn.mock.settledResults === [
  {
    type: 'fulfilled',
    value: 'result',
  },
]
mock.invocationCallOrder

const invocationCallOrder: number[]
This property returns the order of the mock function's execution. It is an array of numbers that are shared between all defined mocks.


const fn1 = vi.fn()
const fn2 = vi.fn()

fn1()
fn2()
fn1()

fn1.mock.invocationCallOrder === [1, 3]
fn2.mock.invocationCallOrder === [2]
mock.contexts

const contexts: ThisParameterType<T>[]
This property is an array of this values used during each call to the mock function.


const fn = vi.fn()
const context = {}

fn.apply(context)
fn.call(context)

fn.mock.contexts[0] === context
fn.mock.contexts[1] === context
mock.instances

const instances: ReturnType<T>[]
This property is an array containing all instances that were created when the mock was called with the new keyword. Note that this is an actual context (this) of the function, not a return value.

WARNING

If mock was instantiated with new MyClass(), then mock.instances will be an array with one value:


const MyClass = vi.fn()
const a = new MyClass()

MyClass.mock.instances[0] === a
If you return a value from constructor, it will not be in instances array, but instead inside results:


const Spy = vi.fn(() => ({ method: vi.fn() }))
const a = new Spy()

Spy.mock.instances[0] !== a
Spy.mock.results[0] === a

# Vitest VI Utility 

Vi
Vitest provides utility functions to help you out through its vi helper. You can access it globally (when globals configuration is enabled), or import it from vitest directly:


import { vi } from 'vitest'
Mock Modules
This section describes the API that you can use when mocking a module. Beware that Vitest doesn't support mocking modules imported using require().

vi.mock
Type: (path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
Type: <T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
Substitutes all imported modules from provided path with another module. You can use configured Vite aliases inside a path. The call to vi.mock is hoisted, so it doesn't matter where you call it. It will always be executed before all imports. If you need to reference some variables outside of its scope, you can define them inside vi.hoisted and reference them inside vi.mock.

WARNING

vi.mock works only for modules that were imported with the import keyword. It doesn't work with require.

In order to hoist vi.mock, Vitest statically analyzes your files. It indicates that vi that was not directly imported from the vitest package (for example, from some utility file) cannot be used. Use vi.mock with vi imported from vitest, or enable globals config option.

Vitest will not mock modules that were imported inside a setup file because they are cached by the time a test file is running. You can call vi.resetModules() inside vi.hoisted to clear all module caches before running a test file.

If the factory function is defined, all imports will return its result. Vitest calls factory only once and caches results for all subsequent imports until vi.unmock or vi.doUnmock is called.

Unlike in jest, the factory can be asynchronous. You can use vi.importActual or a helper with the factory passed in as the first argument, and get the original module inside.

You can also provide an object with a spy property instead of a factory function. If spy is true, then Vitest will automock the module as usual, but it won't override the implementation of exports. This is useful if you just want to assert that the exported method was called correctly by another method.


import { calculator } from './src/calculator.ts'

vi.mock('./src/calculator.ts', { spy: true })

// calls the original implementation,
// but allows asserting the behaviour later
const result = calculator(1, 2)

expect(result).toBe(3)
expect(calculator).toHaveBeenCalledWith(1, 2)
expect(calculator).toHaveReturned(3)
Vitest also supports a module promise instead of a string in the vi.mock and vi.doMock methods for better IDE support. When the file is moved, the path will be updated, and importOriginal inherits the type automatically. Using this signature will also enforce factory return type to be compatible with the original module (keeping exports optional).


vi.mock(import('./path/to/module.js'), async (importOriginal) => {
  const mod = await importOriginal() // type is inferred
  return {
    ...mod,
    // replace some exports
    total: vi.fn(),
  }
})
Under the hood, Vitest still operates on a string and not a module object.

If you are using TypeScript with paths aliases configured in tsconfig.json however, the compiler won't be able to correctly resolve import types. In order to make it work, make sure to replace all aliased imports, with their corresponding relative paths. Eg. use import('./path/to/module.js') instead of import('@/module').

WARNING

vi.mock is hoisted (in other words, moved) to top of the file. It means that whenever you write it (be it inside beforeEach or test), it will actually be called before that.

This also means that you cannot use any variables inside the factory that are defined outside the factory.

If you need to use variables inside the factory, try vi.doMock. It works the same way but isn't hoisted. Beware that it only mocks subsequent imports.

You can also reference variables defined by vi.hoisted method if it was declared before vi.mock:


import { namedExport } from './path/to/module.js'

const mocks = vi.hoisted(() => {
  return {
    namedExport: vi.fn(),
  }
})

vi.mock('./path/to/module.js', () => {
  return {
    namedExport: mocks.namedExport,
  }
})

vi.mocked(namedExport).mockReturnValue(100)

expect(namedExport()).toBe(100)
expect(namedExport).toBe(mocks.namedExport)
WARNING

If you are mocking a module with default export, you will need to provide a default key within the returned factory function object. This is an ES module-specific caveat; therefore, jest documentation may differ as jest uses CommonJS modules. For example,


vi.mock('./path/to/module.js', () => {
  return {
    default: { myDefaultKey: vi.fn() },
    namedExport: vi.fn(),
    // etc...
  }
})
If there is a __mocks__ folder alongside a file that you are mocking, and the factory is not provided, Vitest will try to find a file with the same name in the __mocks__ subfolder and use it as an actual module. If you are mocking a dependency, Vitest will try to find a __mocks__ folder in the root of the project (default is process.cwd()). You can tell Vitest where the dependencies are located through the deps.moduleDirectories config option.

For example, you have this file structure:


- __mocks__
  - axios.js
- src
  __mocks__
    - increment.js
  - increment.js
- tests
  - increment.test.js
If you call vi.mock in a test file without a factory or options provided, it will find a file in the __mocks__ folder to use as a module:

increment.test.js

import { vi } from 'vitest'

// axios is a default export from `__mocks__/axios.js`
import axios from 'axios'

// increment is a named export from `src/__mocks__/increment.js`
import { increment } from '../increment.js'

vi.mock('axios')
vi.mock('../increment.js')

axios.get(`/apples/${increment(1)}`)
WARNING

Beware that if you don't call vi.mock, modules are not mocked automatically. To replicate Jest's automocking behaviour, you can call vi.mock for each required module inside setupFiles.

If there is no __mocks__ folder or a factory provided, Vitest will import the original module and auto-mock all its exports. For the rules applied, see algorithm.

vi.doMock
Type: (path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
Type: <T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
The same as vi.mock, but it's not hoisted to the top of the file, so you can reference variables in the global file scope. The next dynamic import of the module will be mocked.

WARNING

This will not mock modules that were imported before this was called. Don't forget that all static imports in ESM are always hoisted, so putting this before static import will not force it to be called before the import:


vi.doMock('./increment.js') // this will be called _after_ the import statement

import { increment } from './increment.js'
increment.js

export function increment(number) {
  return number + 1
}
increment.test.js

import { beforeEach, test } from 'vitest'
import { increment } from './increment.js'

// the module is not mocked, because vi.doMock is not called yet
increment(1) === 2

let mockedIncrement = 100

beforeEach(() => {
  // you can access variables inside a factory
  vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }))
})

test('importing the next module imports mocked one', async () => {
  // original import WAS NOT MOCKED, because vi.doMock is evaluated AFTER imports
  expect(increment(1)).toBe(2)
  const { increment: mockedIncrement } = await import('./increment.js')
  // new dynamic import returns mocked module
  expect(mockedIncrement(1)).toBe(101)
  expect(mockedIncrement(1)).toBe(102)
  expect(mockedIncrement(1)).toBe(103)
})
vi.mocked
Type: <T>(obj: T, deep?: boolean) => MaybeMockedDeep<T>
Type: <T>(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep<T>
Type helper for TypeScript. Just returns the object that was passed.

When partial is true it will expect a Partial<T> as a return value. By default, this will only make TypeScript believe that the first level values are mocked. You can pass down { deep: true } as a second argument to tell TypeScript that the whole object is mocked, if it actually is.

example.ts

export function add(x: number, y: number): number {
  return x + y
}

export function fetchSomething(): Promise<Response> {
  return fetch('https://vitest.dev/')
}
example.test.ts

import * as example from './example'

vi.mock('./example')

test('1 + 1 equals 10', async () => {
  vi.mocked(example.add).mockReturnValue(10)
  expect(example.add(1, 1)).toBe(10)
})

test('mock return value with only partially correct typing', async () => {
  vi.mocked(example.fetchSomething).mockResolvedValue(new Response('hello'))
  vi.mocked(example.fetchSomething, { partial: true }).mockResolvedValue({ ok: false })
  // vi.mocked(example.someFn).mockResolvedValue({ ok: false }) // this is a type error
})
vi.importActual
Type: <T>(path: string) => Promise<T>
Imports module, bypassing all checks if it should be mocked. Can be useful if you want to mock module partially.


vi.mock('./example.js', async () => {
  const originalModule = await vi.importActual('./example.js')

  return { ...originalModule, get: vi.fn() }
})
vi.importMock
Type: <T>(path: string) => Promise<MaybeMockedDeep<T>>
Imports a module with all of its properties (including nested properties) mocked. Follows the same rules that vi.mock does. For the rules applied, see algorithm.

vi.unmock
Type: (path: string | Promise<Module>) => void
Removes module from the mocked registry. All calls to import will return the original module even if it was mocked before. This call is hoisted to the top of the file, so it will only unmock modules that were defined in setupFiles, for example.

vi.doUnmock
Type: (path: string | Promise<Module>) => void
The same as vi.unmock, but is not hoisted to the top of the file. The next import of the module will import the original module instead of the mock. This will not unmock previously imported modules.

increment.js

export function increment(number) {
  return number + 1
}
increment.test.js

import { increment } from './increment.js'

// increment is already mocked, because vi.mock is hoisted
increment(1) === 100

// this is hoisted, and factory is called before the import on line 1
vi.mock('./increment.js', () => ({ increment: () => 100 }))

// all calls are mocked, and `increment` always returns 100
increment(1) === 100
increment(30) === 100

// this is not hoisted, so other import will return unmocked module
vi.doUnmock('./increment.js')

// this STILL returns 100, because `vi.doUnmock` doesn't reevaluate a module
increment(1) === 100
increment(30) === 100

// the next import is unmocked, now `increment` is the original function that returns count + 1
const { increment: unmockedIncrement } = await import('./increment.js')

unmockedIncrement(1) === 2
unmockedIncrement(30) === 31
vi.resetModules
Type: () => Vitest
Resets modules registry by clearing the cache of all modules. This allows modules to be reevaluated when reimported. Top-level imports cannot be re-evaluated. Might be useful to isolate modules where local state conflicts between tests.


import { vi } from 'vitest'

import { data } from './data.js' // Will not get reevaluated beforeEach test

beforeEach(() => {
  vi.resetModules()
})

test('change state', async () => {
  const mod = await import('./some/path.js') // Will get reevaluated
  mod.changeLocalState('new value')
  expect(mod.getLocalState()).toBe('new value')
})

test('module has old state', async () => {
  const mod = await import('./some/path.js') // Will get reevaluated
  expect(mod.getLocalState()).toBe('old value')
})
WARNING

Does not reset mocks registry. To clear mocks registry, use vi.unmock or vi.doUnmock.

vi.dynamicImportSettled
Wait for all imports to load. Useful, if you have a synchronous call that starts importing a module that you cannot wait otherwise.


import { expect, test } from 'vitest'

// cannot track import because Promise is not returned
function renderComponent() {
  import('./component.js').then(({ render }) => {
    render()
  })
}

test('operations are resolved', async () => {
  renderComponent()
  await vi.dynamicImportSettled()
  expect(document.querySelector('.component')).not.toBeNull()
})
TIP

If during a dynamic import another dynamic import is initiated, this method will wait until all of them are resolved.

This method will also wait for the next setTimeout tick after the import is resolved so all synchronous operations should be completed by the time it's resolved.

Mocking Functions and Objects
This section describes how to work with method mocks and replace environmental and global variables.

vi.fn
Type: (fn?: Function) => Mock
Creates a spy on a function, though can be initiated without one. Every time a function is invoked, it stores its call arguments, returns, and instances. Also, you can manipulate its behavior with methods. If no function is given, mock will return undefined, when invoked.


const getApples = vi.fn(() => 0)

getApples()

expect(getApples).toHaveBeenCalled()
expect(getApples).toHaveReturnedWith(0)

getApples.mockReturnValueOnce(5)

const res = getApples()
expect(res).toBe(5)
expect(getApples).toHaveNthReturnedWith(2, 5)
vi.mockObject 3.2.0+
Type: <T>(value: T) => MaybeMockedDeep<T>
Deeply mocks properties and methods of a given object in the same way as vi.mock() mocks module exports. See automocking for the detail.


const original = {
  simple: () => 'value',
  nested: {
    method: () => 'real'
  },
  prop: 'foo',
}

const mocked = vi.mockObject(original)
expect(mocked.simple()).toBe(undefined)
expect(mocked.nested.method()).toBe(undefined)
expect(mocked.prop).toBe('foo')

mocked.simple.mockReturnValue('mocked')
mocked.nested.method.mockReturnValue('mocked nested')

expect(mocked.simple()).toBe('mocked')
expect(mocked.nested.method()).toBe('mocked nested')
vi.isMockFunction
Type: (fn: Function) => boolean
Checks that a given parameter is a mock function. If you are using TypeScript, it will also narrow down its type.

vi.clearAllMocks
Calls .mockClear() on all spies. This will clear mock history without affecting mock implementations.

vi.resetAllMocks
Calls .mockReset() on all spies. This will clear mock history and reset each mock's implementation to its original.

vi.restoreAllMocks
Calls .mockRestore() on all spies. This will clear mock history, restore all original mock implementations, and restore original descriptors of spied-on objects.

vi.spyOn
Type: <T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
Creates a spy on a method or getter/setter of an object similar to vi.fn(). It returns a mock function.


let apples = 0
const cart = {
  getApples: () => 42,
}

const spy = vi.spyOn(cart, 'getApples').mockImplementation(() => apples)
apples = 1

expect(cart.getApples()).toBe(1)

expect(spy).toHaveBeenCalled()
expect(spy).toHaveReturnedWith(1)
TIP

In environments that support Explicit Resource Management, you can use using instead of const to automatically call mockRestore on any mocked function when the containing block is exited. This is especially useful for spied methods:


it('calls console.log', () => {
  using spy = vi.spyOn(console, 'log').mockImplementation(() => {})
  debug('message')
  expect(spy).toHaveBeenCalled()
})
// console.log is restored here
TIP

You can call vi.restoreAllMocks inside afterEach (or enable test.restoreMocks) to restore all methods to their original implementations. This will restore the original object descriptor, so you won't be able to change method's implementation:


const cart = {
  getApples: () => 42,
}

const spy = vi.spyOn(cart, 'getApples').mockReturnValue(10)

console.log(cart.getApples()) // 10
vi.restoreAllMocks()
console.log(cart.getApples()) // 42
spy.mockReturnValue(10)
console.log(cart.getApples()) // still 42!
TIP

It is not possible to spy on exported methods in Browser Mode. Instead, you can spy on every exported method by calling vi.mock("./file-path.js", { spy: true }). This will mock every export but keep its implementation intact, allowing you to assert if the method was called correctly.


import { calculator } from './src/calculator.ts'

vi.mock('./src/calculator.ts', { spy: true })

calculator(1, 2)

expect(calculator).toHaveBeenCalledWith(1, 2)
expect(calculator).toHaveReturned(3)
And while it is possible to spy on exports in jsdom or other Node.js environments, this might change in the future.

vi.stubEnv
Type: <T extends string>(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest
Changes the value of environmental variable on process.env and import.meta.env. You can restore its value by calling vi.unstubAllEnvs.


import { vi } from 'vitest'

// `process.env.NODE_ENV` and `import.meta.env.NODE_ENV`
// are "development" before calling "vi.stubEnv"

vi.stubEnv('NODE_ENV', 'production')

process.env.NODE_ENV === 'production'
import.meta.env.NODE_ENV === 'production'

vi.stubEnv('NODE_ENV', undefined)

process.env.NODE_ENV === undefined
import.meta.env.NODE_ENV === undefined

// doesn't change other envs
import.meta.env.MODE === 'development'
TIP

You can also change the value by simply assigning it, but you won't be able to use vi.unstubAllEnvs to restore previous value:


import.meta.env.MODE = 'test'
vi.unstubAllEnvs
Type: () => Vitest
Restores all import.meta.env and process.env values that were changed with vi.stubEnv. When it's called for the first time, Vitest remembers the original value and will store it, until unstubAllEnvs is called again.


import { vi } from 'vitest'

// `process.env.NODE_ENV` and `import.meta.env.NODE_ENV`
// are "development" before calling stubEnv

vi.stubEnv('NODE_ENV', 'production')

process.env.NODE_ENV === 'production'
import.meta.env.NODE_ENV === 'production'

vi.stubEnv('NODE_ENV', 'staging')

process.env.NODE_ENV === 'staging'
import.meta.env.NODE_ENV === 'staging'

vi.unstubAllEnvs()

// restores to the value that were stored before the first "stubEnv" call
process.env.NODE_ENV === 'development'
import.meta.env.NODE_ENV === 'development'
vi.stubGlobal
Type: (name: string | number | symbol, value: unknown) => Vitest
Changes the value of global variable. You can restore its original value by calling vi.unstubAllGlobals.


import { vi } from 'vitest'

// `innerWidth` is "0" before calling stubGlobal

vi.stubGlobal('innerWidth', 100)

innerWidth === 100
globalThis.innerWidth === 100
// if you are using jsdom or happy-dom
window.innerWidth === 100
TIP

You can also change the value by simply assigning it to globalThis or window (if you are using jsdom or happy-dom environment), but you won't be able to use vi.unstubAllGlobals to restore original value:


globalThis.innerWidth = 100
// if you are using jsdom or happy-dom
window.innerWidth = 100
vi.unstubAllGlobals
Type: () => Vitest
Restores all global values on globalThis/global (and window/top/self/parent, if you are using jsdom or happy-dom environment) that were changed with vi.stubGlobal. When it's called for the first time, Vitest remembers the original value and will store it, until unstubAllGlobals is called again.


import { vi } from 'vitest'

const Mock = vi.fn()

// IntersectionObserver is "undefined" before calling "stubGlobal"

vi.stubGlobal('IntersectionObserver', Mock)

IntersectionObserver === Mock
global.IntersectionObserver === Mock
globalThis.IntersectionObserver === Mock
// if you are using jsdom or happy-dom
window.IntersectionObserver === Mock

vi.unstubAllGlobals()

globalThis.IntersectionObserver === undefined
'IntersectionObserver' in globalThis === false
// throws ReferenceError, because it's not defined
IntersectionObserver === undefined
Fake Timers
This sections describes how to work with fake timers.

vi.advanceTimersByTime
Type: (ms: number) => Vitest
This method will invoke every initiated timer until the specified number of milliseconds is passed or the queue is empty - whatever comes first.


let i = 0
setInterval(() => console.log(++i), 50)

vi.advanceTimersByTime(150)

// log: 1
// log: 2
// log: 3
vi.advanceTimersByTimeAsync
Type: (ms: number) => Promise<Vitest>
This method will invoke every initiated timer until the specified number of milliseconds is passed or the queue is empty - whatever comes first. This will include asynchronously set timers.


let i = 0
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50)

await vi.advanceTimersByTimeAsync(150)

// log: 1
// log: 2
// log: 3
vi.advanceTimersToNextTimer
Type: () => Vitest
Will call next available timer. Useful to make assertions between each timer call. You can chain call it to manage timers by yourself.


let i = 0
setInterval(() => console.log(++i), 50)

vi.advanceTimersToNextTimer() // log: 1
  .advanceTimersToNextTimer() // log: 2
  .advanceTimersToNextTimer() // log: 3
vi.advanceTimersToNextTimerAsync
Type: () => Promise<Vitest>
Will call next available timer and wait until it's resolved if it was set asynchronously. Useful to make assertions between each timer call.


let i = 0
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50)

await vi.advanceTimersToNextTimerAsync() // log: 1
expect(console.log).toHaveBeenCalledWith(1)

await vi.advanceTimersToNextTimerAsync() // log: 2
await vi.advanceTimersToNextTimerAsync() // log: 3
vi.advanceTimersToNextFrame 2.1.0+
Type: () => Vitest
Similar to vi.advanceTimersByTime, but will advance timers by the milliseconds needed to execute callbacks currently scheduled with requestAnimationFrame.


let frameRendered = false

requestAnimationFrame(() => {
  frameRendered = true
})

vi.advanceTimersToNextFrame()

expect(frameRendered).toBe(true)
vi.getTimerCount
Type: () => number
Get the number of waiting timers.

vi.clearAllTimers
Removes all timers that are scheduled to run. These timers will never run in the future.

vi.getMockedSystemTime
Type: () => Date | null
Returns mocked current date. If date is not mocked the method will return null.

vi.getRealSystemTime
Type: () => number
When using vi.useFakeTimers, Date.now calls are mocked. If you need to get real time in milliseconds, you can call this function.

vi.runAllTicks
Type: () => Vitest
Calls every microtask that was queued by process.nextTick. This will also run all microtasks scheduled by themselves.

vi.runAllTimers
Type: () => Vitest
This method will invoke every initiated timer until the timer queue is empty. It means that every timer called during runAllTimers will be fired. If you have an infinite interval, it will throw after 10 000 tries (can be configured with fakeTimers.loopLimit).


let i = 0
setTimeout(() => console.log(++i))
const interval = setInterval(() => {
  console.log(++i)
  if (i === 3) {
    clearInterval(interval)
  }
}, 50)

vi.runAllTimers()

// log: 1
// log: 2
// log: 3
vi.runAllTimersAsync
Type: () => Promise<Vitest>
This method will asynchronously invoke every initiated timer until the timer queue is empty. It means that every timer called during runAllTimersAsync will be fired even asynchronous timers. If you have an infinite interval, it will throw after 10 000 tries (can be configured with fakeTimers.loopLimit).


setTimeout(async () => {
  console.log(await Promise.resolve('result'))
}, 100)

await vi.runAllTimersAsync()

// log: result
vi.runOnlyPendingTimers
Type: () => Vitest
This method will call every timer that was initiated after vi.useFakeTimers call. It will not fire any timer that was initiated during its call.


let i = 0
setInterval(() => console.log(++i), 50)

vi.runOnlyPendingTimers()

// log: 1
vi.runOnlyPendingTimersAsync
Type: () => Promise<Vitest>
This method will asynchronously call every timer that was initiated after vi.useFakeTimers call, even asynchronous ones. It will not fire any timer that was initiated during its call.


setTimeout(() => {
  console.log(1)
}, 100)
setTimeout(() => {
  Promise.resolve().then(() => {
    console.log(2)
    setInterval(() => {
      console.log(3)
    }, 40)
  })
}, 10)

await vi.runOnlyPendingTimersAsync()

// log: 2
// log: 3
// log: 3
// log: 1
vi.setSystemTime
Type: (date: string | number | Date) => void
If fake timers are enabled, this method simulates a user changing the system clock (will affect date related API like hrtime, performance.now or new Date()) - however, it will not fire any timers. If fake timers are not enabled, this method will only mock Date.* calls.

Useful if you need to test anything that depends on the current date - for example Luxon calls inside your code.

Accepts the same string and number arguments as the Date.


const date = new Date(1998, 11, 19)

vi.useFakeTimers()
vi.setSystemTime(date)

expect(Date.now()).toBe(date.valueOf())

vi.useRealTimers()
vi.useFakeTimers
Type: (config?: FakeTimerInstallOpts) => Vitest
To enable mocking timers, you need to call this method. It will wrap all further calls to timers (such as setTimeout, setInterval, clearTimeout, clearInterval, setImmediate, clearImmediate, and Date) until vi.useRealTimers() is called.

Mocking nextTick is not supported when running Vitest inside node:child_process by using --pool=forks. NodeJS uses process.nextTick internally in node:child_process and hangs when it is mocked. Mocking nextTick is supported when running Vitest with --pool=threads.

The implementation is based internally on @sinonjs/fake-timers.

TIP

vi.useFakeTimers() does not automatically mock process.nextTick and queueMicrotask. But you can enable it by specifying the option in toFake argument: vi.useFakeTimers({ toFake: ['nextTick', 'queueMicrotask'] }).

vi.isFakeTimers
Type: () => boolean
Returns true if fake timers are enabled.

vi.useRealTimers
Type: () => Vitest
When timers have run out, you may call this method to return mocked timers to its original implementations. All timers that were scheduled before will be discarded.

Miscellaneous
A set of useful helper functions that Vitest provides.

vi.waitFor
Type: <T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
Wait for the callback to execute successfully. If the callback throws an error or returns a rejected promise it will continue to wait until it succeeds or times out.

If options is set to a number, the effect is equivalent to setting { timeout: options }.

This is very useful when you need to wait for some asynchronous action to complete, for example, when you start a server and need to wait for it to start.


import { expect, test, vi } from 'vitest'
import { createServer } from './server.js'

test('Server started successfully', async () => {
  const server = createServer()

  await vi.waitFor(
    () => {
      if (!server.isReady) {
        throw new Error('Server not started')
      }

      console.log('Server started')
    },
    {
      timeout: 500, // default is 1000
      interval: 20, // default is 50
    }
  )
  expect(server.isReady).toBe(true)
})
It also works for asynchronous callbacks


// @vitest-environment jsdom

import { expect, test, vi } from 'vitest'
import { getDOMElementAsync, populateDOMAsync } from './dom.js'

test('Element exists in a DOM', async () => {
  // start populating DOM
  populateDOMAsync()

  const element = await vi.waitFor(async () => {
    // try to get the element until it exists
    const element = await getDOMElementAsync() as HTMLElement | null
    expect(element).toBeTruthy()
    expect(element.dataset.initialized).toBeTruthy()
    return element
  }, {
    timeout: 500, // default is 1000
    interval: 20, // default is 50
  })
  expect(element).toBeInstanceOf(HTMLElement)
})
If vi.useFakeTimers is used, vi.waitFor automatically calls vi.advanceTimersByTime(interval) in every check callback.

vi.waitUntil
Type: <T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
This is similar to vi.waitFor, but if the callback throws any errors, execution is immediately interrupted and an error message is received. If the callback returns falsy value, the next check will continue until truthy value is returned. This is useful when you need to wait for something to exist before taking the next step.

Look at the example below. We can use vi.waitUntil to wait for the element to appear on the page, and then we can do something with the element.


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

test('Element render correctly', async () => {
  const element = await vi.waitUntil(
    () => document.querySelector('.element'),
    {
      timeout: 500, // default is 1000
      interval: 20, // default is 50
    }
  )

  // do something with the element
  expect(element.querySelector('.element-child')).toBeTruthy()
})
vi.hoisted
Type: <T>(factory: () => T) => T
All static import statements in ES modules are hoisted to the top of the file, so any code that is defined before the imports will actually be executed after imports are evaluated.

However, it can be useful to invoke some side effects like mocking dates before importing a module.

To bypass this limitation, you can rewrite static imports into dynamic ones like this:


callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
When running vitest, you can do this automatically by using vi.hoisted method. Under the hood, Vitest will convert static imports into dynamic ones with preserved live-bindings.


- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
IMPORTS ARE NOT AVAILABLE

Running code before the imports means that you cannot access imported variables because they are not defined yet:


import { value } from './some/module.js'

vi.hoisted(() => { value }) // throws an error
This code will produce an error:


Cannot access '__vi_import_0__' before initialization
If you need to access a variable from another module inside of vi.hoisted, use dynamic import:


await vi.hoisted(async () => {
  const { value } = await import('./some/module.js')
})
However, it is discourage to import anything inside of vi.hoisted because imports are already hoisted - if you need to execute something before the tests are running, just execute it in the imported module itself.

This method returns the value that was returned from the factory. You can use that value in your vi.mock factories if you need easy access to locally defined variables:


import { expect, vi } from 'vitest'
import { originalMethod } from './path/to/module.js'

const { mockedMethod } = vi.hoisted(() => {
  return { mockedMethod: vi.fn() }
})

vi.mock('./path/to/module.js', () => {
  return { originalMethod: mockedMethod }
})

mockedMethod.mockReturnValue(100)
expect(originalMethod()).toBe(100)
Note that this method can also be called asynchronously even if your environment doesn't support top-level await:


const json = await vi.hoisted(async () => {
  const response = await fetch('https://jsonplaceholder.typicode.com/posts')
  return response.json()
})
vi.setConfig
Type: RuntimeConfig
Updates config for the current test file. This method supports only config options that will affect the current test file:


vi.setConfig({
  allowOnly: true,
  testTimeout: 10_000,
  hookTimeout: 10_000,
  clearMocks: true,
  restoreMocks: true,
  fakeTimers: {
    now: new Date(2021, 11, 19),
    // supports the whole object
  },
  maxConcurrency: 10,
  sequence: {
    hooks: 'stack'
    // supports only "sequence.hooks"
  }
})
vi.resetConfig
Type: RuntimeConfig
If vi.setConfig was called before, this will reset config to the original state.

# Vitest expect

expect
The following types are used in the type signatures below


type Awaitable<T> = T | PromiseLike<T>
expect is used to create assertions. In this context assertions are functions that can be called to assert a statement. Vitest provides chai assertions by default and also Jest compatible assertions built on top of chai. Unlike Jest, Vitest supports a message as the second argument - if the assertion fails, the error message will be equal to it.


export interface ExpectStatic extends Chai.ExpectStatic, AsymmetricMatchersContaining {
  <T>(actual: T, message?: string): Assertion<T>
  extend: (expects: MatchersObject) => void
  anything: () => any
  any: (constructor: unknown) => any
  getState: () => MatcherState
  setState: (state: Partial<MatcherState>) => void
  not: AsymmetricMatchersContaining
}
For example, this code asserts that an input value is equal to 2. If it's not, the assertion will throw an error, and the test will fail.


import { expect } from 'vitest'

const input = Math.sqrt(4)

expect(input).to.equal(2) // chai API
expect(input).toBe(2) // jest API
Technically this example doesn't use test function, so in the console you will see Node.js error instead of Vitest output. To learn more about test, please read Test API Reference.

Also, expect can be used statically to access matcher functions, described later, and more.

WARNING

expect has no effect on testing types, if the expression doesn't have a type error. If you want to use Vitest as type checker, use expectTypeOf or assertType.

soft
Type: ExpectStatic & (actual: any) => Assertions
expect.soft functions similarly to expect, but instead of terminating the test execution upon a failed assertion, it continues running and marks the failure as a test failure. All errors encountered during the test will be displayed until the test is completed.


import { expect, test } from 'vitest'

test('expect.soft test', () => {
  expect.soft(1 + 1).toBe(3) // mark the test as fail and continue
  expect.soft(1 + 2).toBe(4) // mark the test as fail and continue
})
// reporter will report both errors at the end of the run
It can also be used with expect. if expect assertion fails, the test will be terminated and all errors will be displayed.


import { expect, test } from 'vitest'

test('expect.soft test', () => {
  expect.soft(1 + 1).toBe(3) // mark the test as fail and continue
  expect(1 + 2).toBe(4) // failed and terminate the test, all previous errors will be output
  expect.soft(1 + 3).toBe(5) // do not run
})
WARNING

expect.soft can only be used inside the test function.

poll

interface ExpectPoll extends ExpectStatic {
  (actual: () => T, options: { interval; timeout; message }): Promise<Assertions<T>>
}
expect.poll reruns the assertion until it is succeeded. You can configure how many times Vitest should rerun the expect.poll callback by setting interval and timeout options.

If an error is thrown inside the expect.poll callback, Vitest will retry again until the timeout runs out.


import { expect, test } from 'vitest'

test('element exists', async () => {
  asyncInjectElement()

  await expect.poll(() => document.querySelector('.element')).toBeTruthy()
})
WARNING

expect.poll makes every assertion asynchronous, so you need to await it. Since Vitest 3, if you forget to await it, the test will fail with a warning to do so.

expect.poll doesn't work with several matchers:

Snapshot matchers are not supported because they will always succeed. If your condition is flaky, consider using vi.waitFor instead to resolve it first:

import { expect, vi } from 'vitest'

const flakyValue = await vi.waitFor(() => getFlakyValue())
expect(flakyValue).toMatchSnapshot()
.resolves and .rejects are not supported. expect.poll already awaits the condition if it's asynchronous.
toThrow and its aliases are not supported because the expect.poll condition is always resolved before the matcher gets the value
not
Using not will negate the assertion. For example, this code asserts that an input value is not equal to 2. If it's equal, the assertion will throw an error, and the test will fail.


import { expect, test } from 'vitest'

const input = Math.sqrt(16)

expect(input).not.to.equal(2) // chai API
expect(input).not.toBe(2) // jest API
toBe
Type: (value: any) => Awaitable<void>
toBe can be used to assert if primitives are equal or that objects share the same reference. It is equivalent of calling expect(Object.is(3, 3)).toBe(true). If the objects are not the same, but you want to check if their structures are identical, you can use toEqual.

For example, the code below checks if the trader has 13 apples.


import { expect, test } from 'vitest'

const stock = {
  type: 'apples',
  count: 13,
}

test('stock has 13 apples', () => {
  expect(stock.type).toBe('apples')
  expect(stock.count).toBe(13)
})

test('stocks are the same', () => {
  const refStock = stock // same reference

  expect(stock).toBe(refStock)
})
Try not to use toBe with floating-point numbers. Since JavaScript rounds them, 0.1 + 0.2 is not strictly 0.3. To reliably assert floating-point numbers, use toBeCloseTo assertion.

toBeCloseTo
Type: (value: number, numDigits?: number) => Awaitable<void>
Use toBeCloseTo to compare floating-point numbers. The optional numDigits argument limits the number of digits to check after the decimal point. For example:


import { expect, test } from 'vitest'

test.fails('decimals are not equal in javascript', () => {
  expect(0.2 + 0.1).toBe(0.3) // 0.2 + 0.1 is 0.30000000000000004
})

test('decimals are rounded to 5 after the point', () => {
  // 0.2 + 0.1 is 0.30000 | "000000000004" removed
  expect(0.2 + 0.1).toBeCloseTo(0.3, 5)
  // nothing from 0.30000000000000004 is removed
  expect(0.2 + 0.1).not.toBeCloseTo(0.3, 50)
})
toBeDefined
Type: () => Awaitable<void>
toBeDefined asserts that the value is not equal to undefined. Useful use case would be to check if function returned anything.


import { expect, test } from 'vitest'

function getApples() {
  return 3
}

test('function returned something', () => {
  expect(getApples()).toBeDefined()
})
toBeUndefined
Type: () => Awaitable<void>
Opposite of toBeDefined, toBeUndefined asserts that the value is equal to undefined. Useful use case would be to check if function hasn't returned anything.


import { expect, test } from 'vitest'

function getApplesFromStock(stock: string) {
  if (stock === 'Bill') {
    return 13
  }
}

test('mary doesn\'t have a stock', () => {
  expect(getApplesFromStock('Mary')).toBeUndefined()
})
toBeTruthy
Type: () => Awaitable<void>
toBeTruthy asserts that the value is true when converted to boolean. Useful if you don't care for the value, but just want to know it can be converted to true.

For example, having this code you don't care for the return value of stocks.getInfo - it maybe a complex object, a string, or anything else. The code will still work.


import { Stocks } from './stocks.js'

const stocks = new Stocks()
stocks.sync('Bill')
if (stocks.getInfo('Bill')) {
  stocks.sell('apples', 'Bill')
}
So if you want to test that stocks.getInfo will be truthy, you could write:


import { expect, test } from 'vitest'
import { Stocks } from './stocks.js'

const stocks = new Stocks()

test('if we know Bill stock, sell apples to him', () => {
  stocks.sync('Bill')
  expect(stocks.getInfo('Bill')).toBeTruthy()
})
Everything in JavaScript is truthy, except false, null, undefined, NaN, 0, -0, 0n, "" and document.all.

toBeFalsy
Type: () => Awaitable<void>
toBeFalsy asserts that the value is false when converted to boolean. Useful if you don't care for the value, but just want to know if it can be converted to false.

For example, having this code you don't care for the return value of stocks.stockFailed - it may return any falsy value, but the code will still work.


import { Stocks } from './stocks.js'

const stocks = new Stocks()
stocks.sync('Bill')
if (!stocks.stockFailed('Bill')) {
  stocks.sell('apples', 'Bill')
}
So if you want to test that stocks.stockFailed will be falsy, you could write:


import { expect, test } from 'vitest'
import { Stocks } from './stocks.js'

const stocks = new Stocks()

test('if Bill stock hasn\'t failed, sell apples to him', () => {
  stocks.syncStocks('Bill')
  expect(stocks.stockFailed('Bill')).toBeFalsy()
})
Everything in JavaScript is truthy, except false, null, undefined, NaN, 0, -0, 0n, "" and document.all.

toBeNull
Type: () => Awaitable<void>
toBeNull simply asserts if something is null. Alias for .toBe(null).


import { expect, test } from 'vitest'

function apples() {
  return null
}

test('we don\'t have apples', () => {
  expect(apples()).toBeNull()
})
toBeNaN
Type: () => Awaitable<void>
toBeNaN simply asserts if something is NaN. Alias for .toBe(NaN).


import { expect, test } from 'vitest'

let i = 0

function getApplesCount() {
  i++
  return i > 1 ? Number.NaN : i
}

test('getApplesCount has some unusual side effects...', () => {
  expect(getApplesCount()).not.toBeNaN()
  expect(getApplesCount()).toBeNaN()
})
toBeOneOf
Type: (sample: Array<any>) => any
toBeOneOf asserts if a value matches any of the values in the provided array.


import { expect, test } from 'vitest'

test('fruit is one of the allowed values', () => {
  expect(fruit).toBeOneOf(['apple', 'banana', 'orange'])
})
The asymmetric matcher is particularly useful when testing optional properties that could be either null or undefined:


test('optional properties can be null or undefined', () => {
  const user = {
    firstName: 'John',
    middleName: undefined,
    lastName: 'Doe'
  }

  expect(user).toEqual({
    firstName: expect.any(String),
    middleName: expect.toBeOneOf([expect.any(String), undefined]),
    lastName: expect.any(String),
  })
})
TIP

You can use expect.not with this matcher to ensure a value does NOT match any of the provided options.

toBeTypeOf
Type: (c: 'bigint' | 'boolean' | 'function' | 'number' | 'object' | 'string' | 'symbol' | 'undefined') => Awaitable<void>
toBeTypeOf asserts if an actual value is of type of received type.


import { expect, test } from 'vitest'

const actual = 'stock'

test('stock is type of string', () => {
  expect(actual).toBeTypeOf('string')
})
toBeInstanceOf
Type: (c: any) => Awaitable<void>
toBeInstanceOf asserts if an actual value is instance of received class.


import { expect, test } from 'vitest'
import { Stocks } from './stocks.js'

const stocks = new Stocks()

test('stocks are instance of Stocks', () => {
  expect(stocks).toBeInstanceOf(Stocks)
})
toBeGreaterThan
Type: (n: number | bigint) => Awaitable<void>
toBeGreaterThan asserts if actual value is greater than received one. Equal values will fail the test.


import { expect, test } from 'vitest'
import { getApples } from './stocks.js'

test('have more then 10 apples', () => {
  expect(getApples()).toBeGreaterThan(10)
})
toBeGreaterThanOrEqual
Type: (n: number | bigint) => Awaitable<void>
toBeGreaterThanOrEqual asserts if actual value is greater than received one or equal to it.


import { expect, test } from 'vitest'
import { getApples } from './stocks.js'

test('have 11 apples or more', () => {
  expect(getApples()).toBeGreaterThanOrEqual(11)
})
toBeLessThan
Type: (n: number | bigint) => Awaitable<void>
toBeLessThan asserts if actual value is less than received one. Equal values will fail the test.


import { expect, test } from 'vitest'
import { getApples } from './stocks.js'

test('have less then 20 apples', () => {
  expect(getApples()).toBeLessThan(20)
})
toBeLessThanOrEqual
Type: (n: number | bigint) => Awaitable<void>
toBeLessThanOrEqual asserts if actual value is less than received one or equal to it.


import { expect, test } from 'vitest'
import { getApples } from './stocks.js'

test('have 11 apples or less', () => {
  expect(getApples()).toBeLessThanOrEqual(11)
})
toEqual
Type: (received: any) => Awaitable<void>
toEqual asserts if actual value is equal to received one or has the same structure, if it is an object (compares them recursively). You can see the difference between toEqual and toBe in this example:


import { expect, test } from 'vitest'

const stockBill = {
  type: 'apples',
  count: 13,
}

const stockMary = {
  type: 'apples',
  count: 13,
}

test('stocks have the same properties', () => {
  expect(stockBill).toEqual(stockMary)
})

test('stocks are not the same', () => {
  expect(stockBill).not.toBe(stockMary)
})
WARNING

For Error objects, non-enumerable properties such as name, message, cause and AggregateError.errors are also compared. For Error.cause, the comparison is done asymmetrically:


// success
expect(new Error('hi', { cause: 'x' })).toEqual(new Error('hi'))

// fail
expect(new Error('hi')).toEqual(new Error('hi', { cause: 'x' }))
To test if something was thrown, use toThrowError assertion.

toStrictEqual
Type: (received: any) => Awaitable<void>
toStrictEqual asserts if the actual value is equal to the received one or has the same structure if it is an object (compares them recursively), and of the same type.

Differences from .toEqual:

Keys with undefined properties are checked. e.g. {a: undefined, b: 2} does not match {b: 2} when using .toStrictEqual.
Array sparseness is checked. e.g. [, 1] does not match [undefined, 1] when using .toStrictEqual.
Object types are checked to be equal. e.g. A class instance with fields a and b will not equal a literal object with fields a and b.

import { expect, test } from 'vitest'

class Stock {
  constructor(type) {
    this.type = type
  }
}

test('structurally the same, but semantically different', () => {
  expect(new Stock('apples')).toEqual({ type: 'apples' })
  expect(new Stock('apples')).not.toStrictEqual({ type: 'apples' })
})
toContain
Type: (received: string) => Awaitable<void>
toContain asserts if the actual value is in an array. toContain can also check whether a string is a substring of another string. If you are running tests in a browser-like environment, this assertion can also check if class is contained in a classList, or an element is inside another one.


import { expect, test } from 'vitest'
import { getAllFruits } from './stocks.js'

test('the fruit list contains orange', () => {
  expect(getAllFruits()).toContain('orange')

  const element = document.querySelector('#el')
  // element has a class
  expect(element.classList).toContain('flex')
  // element is inside another one
  expect(document.querySelector('#wrapper')).toContain(element)
})
toContainEqual
Type: (received: any) => Awaitable<void>
toContainEqual asserts if an item with a specific structure and values is contained in an array. It works like toEqual inside for each element.


import { expect, test } from 'vitest'
import { getFruitStock } from './stocks.js'

test('apple available', () => {
  expect(getFruitStock()).toContainEqual({ fruit: 'apple', count: 5 })
})
toHaveLength
Type: (received: number) => Awaitable<void>
toHaveLength asserts if an object has a .length property and it is set to a certain numeric value.


import { expect, test } from 'vitest'

test('toHaveLength', () => {
  expect('abc').toHaveLength(3)
  expect([1, 2, 3]).toHaveLength(3)

  expect('').not.toHaveLength(3) // doesn't have .length of 3
  expect({ length: 3 }).toHaveLength(3)
})
toHaveProperty
Type: (key: any, received?: any) => Awaitable<void>
toHaveProperty asserts if a property at provided reference key exists for an object.

You can provide an optional value argument also known as deep equality, like the toEqual matcher to compare the received property value.


import { expect, test } from 'vitest'

const invoice = {
  'isActive': true,
  'P.O': '12345',
  'customer': {
    first_name: 'John',
    last_name: 'Doe',
    location: 'China',
  },
  'total_amount': 5000,
  'items': [
    {
      type: 'apples',
      quantity: 10,
    },
    {
      type: 'oranges',
      quantity: 5,
    },
  ],
}

test('John Doe Invoice', () => {
  expect(invoice).toHaveProperty('isActive') // assert that the key exists
  expect(invoice).toHaveProperty('total_amount', 5000) // assert that the key exists and the value is equal

  expect(invoice).not.toHaveProperty('account') // assert that this key does not exist

  // Deep referencing using dot notation
  expect(invoice).toHaveProperty('customer.first_name')
  expect(invoice).toHaveProperty('customer.last_name', 'Doe')
  expect(invoice).not.toHaveProperty('customer.location', 'India')

  // Deep referencing using an array containing the key
  expect(invoice).toHaveProperty('items[0].type', 'apples')
  expect(invoice).toHaveProperty('items.0.type', 'apples') // dot notation also works

  // Deep referencing using an array containing the keyPath
  expect(invoice).toHaveProperty(['items', 0, 'type'], 'apples')
  expect(invoice).toHaveProperty(['items', '0', 'type'], 'apples') // string notation also works

  // Wrap your key in an array to avoid the key from being parsed as a deep reference
  expect(invoice).toHaveProperty(['P.O'], '12345')
})
toMatch
Type: (received: string | regexp) => Awaitable<void>
toMatch asserts if a string matches a regular expression or a string.


import { expect, test } from 'vitest'

test('top fruits', () => {
  expect('top fruits include apple, orange and grape').toMatch(/apple/)
  expect('applefruits').toMatch('fruit') // toMatch also accepts a string
})
toMatchObject
Type: (received: object | array) => Awaitable<void>
toMatchObject asserts if an object matches a subset of the properties of an object.

You can also pass an array of objects. This is useful if you want to check that two arrays match in their number of elements, as opposed to arrayContaining, which allows for extra elements in the received array.


import { expect, test } from 'vitest'

const johnInvoice = {
  isActive: true,
  customer: {
    first_name: 'John',
    last_name: 'Doe',
    location: 'China',
  },
  total_amount: 5000,
  items: [
    {
      type: 'apples',
      quantity: 10,
    },
    {
      type: 'oranges',
      quantity: 5,
    },
  ],
}

const johnDetails = {
  customer: {
    first_name: 'John',
    last_name: 'Doe',
    location: 'China',
  },
}

test('invoice has john personal details', () => {
  expect(johnInvoice).toMatchObject(johnDetails)
})

test('the number of elements must match exactly', () => {
  // Assert that an array of object matches
  expect([{ foo: 'bar' }, { baz: 1 }]).toMatchObject([
    { foo: 'bar' },
    { baz: 1 },
  ])
})
toThrowError
Type: (received: any) => Awaitable<void>

Alias: toThrow

toThrowError asserts if a function throws an error when it is called.

You can provide an optional argument to test that a specific error is thrown:

RegExp: error message matches the pattern
string: error message includes the substring
Error, AsymmetricMatcher: compare with a received object similar to toEqual(received)
TIP

You must wrap the code in a function, otherwise the error will not be caught, and test will fail.

This does not apply for async calls as rejects correctly unwraps the promise:


test('expect rejects toThrow', async ({ expect }) => {
  const promise = Promise.reject(new Error('Test'))
  await expect(promise).rejects.toThrowError()
})
For example, if we want to test that getFruitStock('pineapples') throws, we could write:


import { expect, test } from 'vitest'

function getFruitStock(type: string) {
  if (type === 'pineapples') {
    throw new Error('Pineapples are not in stock')
  }

  // Do some other stuff
}

test('throws on pineapples', () => {
  // Test that the error message says "stock" somewhere: these are equivalent
  expect(() => getFruitStock('pineapples')).toThrowError(/stock/)
  expect(() => getFruitStock('pineapples')).toThrowError('stock')

  // Test the exact error message
  expect(() => getFruitStock('pineapples')).toThrowError(
    /^Pineapples are not in stock$/,
  )

  expect(() => getFruitStock('pineapples')).toThrowError(
    new Error('Pineapples are not in stock'),
  )
  expect(() => getFruitStock('pineapples')).toThrowError(expect.objectContaining({
    message: 'Pineapples are not in stock',
  }))
})
TIP

To test async functions, use in combination with rejects.


function getAsyncFruitStock() {
  return Promise.reject(new Error('empty'))
}

test('throws on pineapples', async () => {
  await expect(() => getAsyncFruitStock()).rejects.toThrowError('empty')
})
toMatchSnapshot
Type: <T>(shape?: Partial<T> | string, hint?: string) => void
This ensures that a value matches the most recent snapshot.

You can provide an optional hint string argument that is appended to the test name. Although Vitest always appends a number at the end of a snapshot name, short descriptive hints might be more useful than numbers to differentiate multiple snapshots in a single it or test block. Vitest sorts snapshots by name in the corresponding .snap file.

TIP

When a snapshot mismatches and causes the test to fail, if the mismatch is expected, you can press u key to update the snapshot once. Or you can pass -u or --update CLI options to make Vitest always update the tests.


import { expect, test } from 'vitest'

test('matches snapshot', () => {
  const data = { foo: new Set(['bar', 'snapshot']) }
  expect(data).toMatchSnapshot()
})
You can also provide a shape of an object, if you are testing just a shape of an object, and don't need it to be 100% compatible:


import { expect, test } from 'vitest'

test('matches snapshot', () => {
  const data = { foo: new Set(['bar', 'snapshot']) }
  expect(data).toMatchSnapshot({ foo: expect.any(Set) })
})
toMatchInlineSnapshot
Type: <T>(shape?: Partial<T> | string, snapshot?: string, hint?: string) => void
This ensures that a value matches the most recent snapshot.

Vitest adds and updates the inlineSnapshot string argument to the matcher in the test file (instead of an external .snap file).


import { expect, test } from 'vitest'

test('matches inline snapshot', () => {
  const data = { foo: new Set(['bar', 'snapshot']) }
  // Vitest will update following content when updating the snapshot
  expect(data).toMatchInlineSnapshot(`
    {
      "foo": Set {
        "bar",
        "snapshot",
      },
    }
  `)
})
You can also provide a shape of an object, if you are testing just a shape of an object, and don't need it to be 100% compatible:


import { expect, test } from 'vitest'

test('matches snapshot', () => {
  const data = { foo: new Set(['bar', 'snapshot']) }
  expect(data).toMatchInlineSnapshot(
    { foo: expect.any(Set) },
    `
    {
      "foo": Any<Set>,
    }
  `
  )
})
toMatchFileSnapshot
Type: <T>(filepath: string, hint?: string) => Promise<void>
Compare or update the snapshot with the content of a file explicitly specified (instead of the .snap file).


import { expect, it } from 'vitest'

it('render basic', async () => {
  const result = renderHTML(h('div', { class: 'foo' }))
  await expect(result).toMatchFileSnapshot('./test/basic.output.html')
})
Note that since file system operation is async, you need to use await with toMatchFileSnapshot(). If await is not used, Vitest treats it like expect.soft, meaning the code after the statement will continue to run even if the snapshot mismatches. After the test finishes, Vitest will check the snapshot and fail if there is a mismatch.

toThrowErrorMatchingSnapshot
Type: (hint?: string) => void
The same as toMatchSnapshot, but expects the same value as toThrowError.

toThrowErrorMatchingInlineSnapshot
Type: (snapshot?: string, hint?: string) => void
The same as toMatchInlineSnapshot, but expects the same value as toThrowError.

toHaveBeenCalled
Type: () => Awaitable<void>
This assertion is useful for testing that a function has been called. Requires a spy function to be passed to expect.


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

const market = {
  buy(subject: string, amount: number) {
    // ...
  },
}

test('spy function', () => {
  const buySpy = vi.spyOn(market, 'buy')

  expect(buySpy).not.toHaveBeenCalled()

  market.buy('apples', 10)

  expect(buySpy).toHaveBeenCalled()
})
toHaveBeenCalledTimes
Type: (amount: number) => Awaitable<void>
This assertion checks if a function was called a certain amount of times. Requires a spy function to be passed to expect.


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

const market = {
  buy(subject: string, amount: number) {
    // ...
  },
}

test('spy function called two times', () => {
  const buySpy = vi.spyOn(market, 'buy')

  market.buy('apples', 10)
  market.buy('apples', 20)

  expect(buySpy).toHaveBeenCalledTimes(2)
})
toHaveBeenCalledWith
Type: (...args: any[]) => Awaitable<void>
This assertion checks if a function was called at least once with certain parameters. Requires a spy function to be passed to expect.


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

const market = {
  buy(subject: string, amount: number) {
    // ...
  },
}

test('spy function', () => {
  const buySpy = vi.spyOn(market, 'buy')

  market.buy('apples', 10)
  market.buy('apples', 20)

  expect(buySpy).toHaveBeenCalledWith('apples', 10)
  expect(buySpy).toHaveBeenCalledWith('apples', 20)
})
toHaveBeenCalledBefore 3.0.0+
Type: (mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable<void>
This assertion checks if a Mock was called before another Mock.


test('calls mock1 before mock2', () => {
  const mock1 = vi.fn()
  const mock2 = vi.fn()

  mock1()
  mock2()
  mock1()

  expect(mock1).toHaveBeenCalledBefore(mock2)
})
toHaveBeenCalledAfter 3.0.0+
Type: (mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable<void>
This assertion checks if a Mock was called after another Mock.


test('calls mock1 after mock2', () => {
  const mock1 = vi.fn()
  const mock2 = vi.fn()

  mock2()
  mock1()
  mock2()

  expect(mock1).toHaveBeenCalledAfter(mock2)
})
toHaveBeenCalledExactlyOnceWith 3.0.0+
Type: (...args: any[]) => Awaitable<void>
This assertion checks if a function was called exactly once and with certain parameters. Requires a spy function to be passed to expect.


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

const market = {
  buy(subject: string, amount: number) {
    // ...
  },
}

test('spy function', () => {
  const buySpy = vi.spyOn(market, 'buy')

  market.buy('apples', 10)

  expect(buySpy).toHaveBeenCalledExactlyOnceWith('apples', 10)
})
toHaveBeenLastCalledWith
Type: (...args: any[]) => Awaitable<void>
This assertion checks if a function was called with certain parameters at its last invocation. Requires a spy function to be passed to expect.


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

const market = {
  buy(subject: string, amount: number) {
    // ...
  },
}

test('spy function', () => {
  const buySpy = vi.spyOn(market, 'buy')

  market.buy('apples', 10)
  market.buy('apples', 20)

  expect(buySpy).not.toHaveBeenLastCalledWith('apples', 10)
  expect(buySpy).toHaveBeenLastCalledWith('apples', 20)
})
toHaveBeenNthCalledWith
Type: (time: number, ...args: any[]) => Awaitable<void>
This assertion checks if a function was called with certain parameters at the certain time. The count starts at 1. So, to check the second entry, you would write .toHaveBeenNthCalledWith(2, ...).

Requires a spy function to be passed to expect.


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

const market = {
  buy(subject: string, amount: number) {
    // ...
  },
}

test('first call of spy function called with right params', () => {
  const buySpy = vi.spyOn(market, 'buy')

  market.buy('apples', 10)
  market.buy('apples', 20)

  expect(buySpy).toHaveBeenNthCalledWith(1, 'apples', 10)
})
toHaveReturned
Type: () => Awaitable<void>
This assertion checks if a function has successfully returned a value at least once (i.e., did not throw an error). Requires a spy function to be passed to expect.


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

function getApplesPrice(amount: number) {
  const PRICE = 10
  return amount * PRICE
}

test('spy function returned a value', () => {
  const getPriceSpy = vi.fn(getApplesPrice)

  const price = getPriceSpy(10)

  expect(price).toBe(100)
  expect(getPriceSpy).toHaveReturned()
})
toHaveReturnedTimes
Type: (amount: number) => Awaitable<void>
This assertion checks if a function has successfully returned a value an exact amount of times (i.e., did not throw an error). Requires a spy function to be passed to expect.


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

test('spy function returns a value two times', () => {
  const sell = vi.fn((product: string) => ({ product }))

  sell('apples')
  sell('bananas')

  expect(sell).toHaveReturnedTimes(2)
})
toHaveReturnedWith
Type: (returnValue: any) => Awaitable<void>
You can call this assertion to check if a function has successfully returned a value with certain parameters at least once. Requires a spy function to be passed to expect.


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

test('spy function returns a product', () => {
  const sell = vi.fn((product: string) => ({ product }))

  sell('apples')

  expect(sell).toHaveReturnedWith({ product: 'apples' })
})
toHaveLastReturnedWith
Type: (returnValue: any) => Awaitable<void>
You can call this assertion to check if a function has successfully returned a certain value when it was last invoked. Requires a spy function to be passed to expect.


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

test('spy function returns bananas on a last call', () => {
  const sell = vi.fn((product: string) => ({ product }))

  sell('apples')
  sell('bananas')

  expect(sell).toHaveLastReturnedWith({ product: 'bananas' })
})
toHaveNthReturnedWith
Type: (time: number, returnValue: any) => Awaitable<void>
You can call this assertion to check if a function has successfully returned a value with certain parameters on a certain call. Requires a spy function to be passed to expect.


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

test('spy function returns bananas on second call', () => {
  const sell = vi.fn((product: string) => ({ product }))

  sell('apples')
  sell('bananas')

  expect(sell).toHaveNthReturnedWith(2, { product: 'bananas' })
})
toHaveResolved
Type: () => Awaitable<void>
This assertion checks if a function has successfully resolved a value at least once (i.e., did not reject). Requires a spy function to be passed to expect.

If the function returned a promise, but it was not resolved yet, this will fail.


import { expect, test, vi } from 'vitest'
import db from './db/apples.js'

async function getApplesPrice(amount: number) {
  return amount * await db.get('price')
}

test('spy function resolved a value', async () => {
  const getPriceSpy = vi.fn(getApplesPrice)

  const price = await getPriceSpy(10)

  expect(price).toBe(100)
  expect(getPriceSpy).toHaveResolved()
})
toHaveResolvedTimes
Type: (amount: number) => Awaitable<void>
This assertion checks if a function has successfully resolved a value an exact amount of times (i.e., did not reject). Requires a spy function to be passed to expect.

This will only count resolved promises. If the function returned a promise, but it was not resolved yet, it will not be counted.


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

test('spy function resolved a value two times', async () => {
  const sell = vi.fn((product: string) => Promise.resolve({ product }))

  await sell('apples')
  await sell('bananas')

  expect(sell).toHaveResolvedTimes(2)
})
toHaveResolvedWith
Type: (returnValue: any) => Awaitable<void>
You can call this assertion to check if a function has successfully resolved a certain value at least once. Requires a spy function to be passed to expect.

If the function returned a promise, but it was not resolved yet, this will fail.


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

test('spy function resolved a product', async () => {
  const sell = vi.fn((product: string) => Promise.resolve({ product }))

  await sell('apples')

  expect(sell).toHaveResolvedWith({ product: 'apples' })
})
toHaveLastResolvedWith
Type: (returnValue: any) => Awaitable<void>
You can call this assertion to check if a function has successfully resolved a certain value when it was last invoked. Requires a spy function to be passed to expect.

If the function returned a promise, but it was not resolved yet, this will fail.


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

test('spy function resolves bananas on a last call', async () => {
  const sell = vi.fn((product: string) => Promise.resolve({ product }))

  await sell('apples')
  await sell('bananas')

  expect(sell).toHaveLastResolvedWith({ product: 'bananas' })
})
toHaveNthResolvedWith
Type: (time: number, returnValue: any) => Awaitable<void>
You can call this assertion to check if a function has successfully resolved a certain value on a specific invocation. Requires a spy function to be passed to expect.

If the function returned a promise, but it was not resolved yet, this will fail.


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

test('spy function returns bananas on second call', async () => {
  const sell = vi.fn((product: string) => Promise.resolve({ product }))

  await sell('apples')
  await sell('bananas')

  expect(sell).toHaveNthResolvedWith(2, { product: 'bananas' })
})
toSatisfy
Type: (predicate: (value: any) => boolean) => Awaitable<void>
This assertion checks if a value satisfies a certain predicate.


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

const isOdd = (value: number) => value % 2 !== 0

describe('toSatisfy()', () => {
  it('pass with 0', () => {
    expect(1).toSatisfy(isOdd)
  })

  it('pass with negation', () => {
    expect(2).not.toSatisfy(isOdd)
  })
})
resolves
Type: Promisify<Assertions>
resolves is intended to remove boilerplate when asserting asynchronous code. Use it to unwrap value from the pending promise and assert its value with usual assertions. If the promise rejects, the assertion will fail.

It returns the same Assertions object, but all matchers now return Promise, so you would need to await it. Also works with chai assertions.

For example, if you have a function, that makes an API call and returns some data, you may use this code to assert its return value:


import { expect, test } from 'vitest'

async function buyApples() {
  return fetch('/buy/apples').then(r => r.json())
}

test('buyApples returns new stock id', async () => {
  // toEqual returns a promise now, so you HAVE to await it
  await expect(buyApples()).resolves.toEqual({ id: 1 }) // jest API
  await expect(buyApples()).resolves.to.equal({ id: 1 }) // chai API
})
WARNING

If the assertion is not awaited, then you will have a false-positive test that will pass every time. To make sure that assertions are actually called, you may use expect.assertions(number).

Since Vitest 3, if a method is not awaited, Vitest will show a warning at the end of the test. In Vitest 4, the test will be marked as "failed" if the assertion is not awaited.

rejects
Type: Promisify<Assertions>
rejects is intended to remove boilerplate when asserting asynchronous code. Use it to unwrap reason why the promise was rejected, and assert its value with usual assertions. If the promise successfully resolves, the assertion will fail.

It returns the same Assertions object, but all matchers now return Promise, so you would need to await it. Also works with chai assertions.

For example, if you have a function that fails when you call it, you may use this code to assert the reason:


import { expect, test } from 'vitest'

async function buyApples(id) {
  if (!id) {
    throw new Error('no id')
  }
}

test('buyApples throws an error when no id provided', async () => {
  // toThrow returns a promise now, so you HAVE to await it
  await expect(buyApples()).rejects.toThrow('no id')
})
WARNING

If the assertion is not awaited, then you will have a false-positive test that will pass every time. To make sure that assertions were actually called, you can use expect.assertions(number).

Since Vitest 3, if a method is not awaited, Vitest will show a warning at the end of the test. In Vitest 4, the test will be marked as "failed" if the assertion is not awaited.

expect.assertions
Type: (count: number) => void
After the test has passed or failed verify that a certain number of assertions was called during a test. A useful case would be to check if an asynchronous code was called.

For example, if we have a function that asynchronously calls two matchers, we can assert that they were actually called.


import { expect, test } from 'vitest'

async function doAsync(...cbs) {
  await Promise.all(
    cbs.map((cb, index) => cb({ index })),
  )
}

test('all assertions are called', async () => {
  expect.assertions(2)
  function callback1(data) {
    expect(data).toBeTruthy()
  }
  function callback2(data) {
    expect(data).toBeTruthy()
  }

  await doAsync(callback1, callback2)
})
WARNING

When using assertions with async concurrent tests, expect from the local Test Context must be used to ensure the right test is detected.

expect.hasAssertions
Type: () => void
After the test has passed or failed verify that at least one assertion was called during a test. A useful case would be to check if an asynchronous code was called.

For example, if you have a code that calls a callback, we can make an assertion inside a callback, but the test will always pass if we don't check if an assertion was called.


import { expect, test } from 'vitest'
import { db } from './db.js'

const cbs = []

function onSelect(cb) {
  cbs.push(cb)
}

// after selecting from db, we call all callbacks
function select(id) {
  return db.select({ id }).then((data) => {
    return Promise.all(
      cbs.map(cb => cb(data)),
    )
  })
}

test('callback was called', async () => {
  expect.hasAssertions()
  onSelect((data) => {
    // should be called on select
    expect(data).toBeTruthy()
  })
  // if not awaited, test will fail
  // if you don't have expect.hasAssertions(), test will pass
  await select(3)
})
expect.unreachable
Type: (message?: string) => never
This method is used to assert that a line should never be reached.

For example, if we want to test that build() throws due to receiving directories having no src folder, and also handle each error separately, we could do this:


import { expect, test } from 'vitest'

async function build(dir) {
  if (dir.includes('no-src')) {
    throw new Error(`${dir}/src does not exist`)
  }
}

const errorDirs = [
  'no-src-folder',
  // ...
]

test.each(errorDirs)('build fails with "%s"', async (dir) => {
  try {
    await build(dir)
    expect.unreachable('Should not pass build')
  }
  catch (err: any) {
    expect(err).toBeInstanceOf(Error)
    expect(err.stack).toContain('build')

    switch (dir) {
      case 'no-src-folder':
        expect(err.message).toBe(`${dir}/src does not exist`)
        break
      default:
        // to exhaust all error tests
        expect.unreachable('All error test must be handled')
        break
    }
  }
})
expect.anything
Type: () => any
This asymmetric matcher, when used with equality check, will always return true. Useful, if you just want to be sure that the property exist.


import { expect, test } from 'vitest'

test('object has "apples" key', () => {
  expect({ apples: 22 }).toEqual({ apples: expect.anything() })
})
expect.any
Type: (constructor: unknown) => any
This asymmetric matcher, when used with an equality check, will return true only if the value is an instance of a specified constructor. Useful, if you have a value that is generated each time, and you only want to know that it exists with a proper type.


import { expect, test } from 'vitest'
import { generateId } from './generators.js'

test('"id" is a number', () => {
  expect({ id: generateId() }).toEqual({ id: expect.any(Number) })
})
expect.closeTo
Type: (expected: any, precision?: number) => any
expect.closeTo is useful when comparing floating point numbers in object properties or array item. If you need to compare a number, please use .toBeCloseTo instead.

The optional precision argument limits the number of digits to check after the decimal point. For the default value 2, the test criterion is Math.abs(expected - received) < 0.005 (that is, 10 ** -2 / 2).

For example, this test passes with a precision of 5 digits:


test('compare float in object properties', () => {
  expect({
    title: '0.1 + 0.2',
    sum: 0.1 + 0.2,
  }).toEqual({
    title: '0.1 + 0.2',
    sum: expect.closeTo(0.3, 5),
  })
})
expect.arrayContaining
Type: <T>(expected: T[]) => any
When used with an equality check, this asymmetric matcher will return true if the value is an array and contains specified items.


import { expect, test } from 'vitest'

test('basket includes fuji', () => {
  const basket = {
    varieties: [
      'Empire',
      'Fuji',
      'Gala',
    ],
    count: 3
  }
  expect(basket).toEqual({
    count: 3,
    varieties: expect.arrayContaining(['Fuji'])
  })
})
TIP

You can use expect.not with this matcher to negate the expected value.

expect.objectContaining
Type: (expected: any) => any
When used with an equality check, this asymmetric matcher will return true if the value has a similar shape.


import { expect, test } from 'vitest'

test('basket has empire apples', () => {
  const basket = {
    varieties: [
      {
        name: 'Empire',
        count: 1,
      }
    ],
  }
  expect(basket).toEqual({
    varieties: [
      expect.objectContaining({ name: 'Empire' }),
    ]
  })
})
TIP

You can use expect.not with this matcher to negate the expected value.

expect.stringContaining
Type: (expected: any) => any
When used with an equality check, this asymmetric matcher will return true if the value is a string and contains a specified substring.


import { expect, test } from 'vitest'

test('variety has "Emp" in its name', () => {
  const variety = {
    name: 'Empire',
    count: 1,
  }
  expect(variety).toEqual({
    name: expect.stringContaining('Emp'),
    count: 1,
  })
})
TIP

You can use expect.not with this matcher to negate the expected value.

expect.stringMatching
Type: (expected: any) => any
When used with an equality check, this asymmetric matcher will return true if the value is a string and contains a specified substring or if the string matches a regular expression.


import { expect, test } from 'vitest'

test('variety ends with "re"', () => {
  const variety = {
    name: 'Empire',
    count: 1,
  }
  expect(variety).toEqual({
    name: expect.stringMatching(/re$/),
    count: 1,
  })
})
TIP

You can use expect.not with this matcher to negate the expected value.

expect.addSnapshotSerializer
Type: (plugin: PrettyFormatPlugin) => void
This method adds custom serializers that are called when creating a snapshot. This is an advanced feature - if you want to know more, please read a guide on custom serializers.

If you are adding custom serializers, you should call this method inside setupFiles. This will affect every snapshot.

TIP

If you previously used Vue CLI with Jest, you might want to install jest-serializer-vue. Otherwise, your snapshots will be wrapped in a string, which cases " to be escaped.

expect.extend
Type: (matchers: MatchersObject) => void
You can extend default matchers with your own. This function is used to extend the matchers object with custom matchers.

When you define matchers that way, you also create asymmetric matchers that can be used like expect.stringContaining.


import { expect, test } from 'vitest'

test('custom matchers', () => {
  expect.extend({
    toBeFoo: (received, expected) => {
      if (received !== 'foo') {
        return {
          message: () => `expected ${received} to be foo`,
          pass: false,
        }
      }
    },
  })

  expect('foo').toBeFoo()
  expect({ foo: 'foo' }).toEqual({ foo: expect.toBeFoo() })
})
TIP

If you want your matchers to appear in every test, you should call this method inside setupFiles.

This function is compatible with Jest's expect.extend, so any library that uses it to create custom matchers will work with Vitest.

If you are using TypeScript, since Vitest 0.31.0 you can extend default Assertion interface in an ambient declaration file (e.g: vitest.d.ts) with the code below:


interface CustomMatchers<R = unknown> {
  toBeFoo: () => R
}

declare module 'vitest' {
  interface Assertion<T = any> extends CustomMatchers<T> {}
  interface AsymmetricMatchersContaining extends CustomMatchers {}
}
WARNING

Don't forget to include the ambient declaration file in your tsconfig.json.

TIP

If you want to know more, checkout guide on extending matchers.

expect.addEqualityTesters
Type: (tester: Array<Tester>) => void
You can use this method to define custom testers, which are methods used by matchers, to test if two objects are equal. It is compatible with Jest's expect.addEqualityTesters.


import { expect, test } from 'vitest'

class AnagramComparator {
  public word: string

  constructor(word: string) {
    this.word = word
  }

  equals(other: AnagramComparator): boolean {
    const cleanStr1 = this.word.replace(/ /g, '').toLowerCase()
    const cleanStr2 = other.word.replace(/ /g, '').toLowerCase()

    const sortedStr1 = cleanStr1.split('').sort().join('')
    const sortedStr2 = cleanStr2.split('').sort().join('')

    return sortedStr1 === sortedStr2
  }
}

function isAnagramComparator(a: unknown): a is AnagramComparator {
  return a instanceof AnagramComparator
}

function areAnagramsEqual(a: unknown, b: unknown): boolean | undefined {
  const isAAnagramComparator = isAnagramComparator(a)
  const isBAnagramComparator = isAnagramComparator(b)

  if (isAAnagramComparator && isBAnagramComparator) {
    return a.equals(b)
  }
  else if (isAAnagramComparator === isBAnagramComparator) {
    return undefined
  }
  else {
    return false
  }
}

expect.addEqualityTesters([areAnagramsEqual])

test('custom equality tester', () => {
  expect(new AnagramComparator('listen')).toEqual(new AnagramComparator('silent'))
})


## Mocking

Mocking
When writing tests it's only a matter of time before you need to create a "fake" version of an internal — or external — service. This is commonly referred to as mocking. Vitest provides utility functions to help you out through its vi helper. You can import it from vitest or access it globally if global configuration is enabled.

WARNING

Always remember to clear or restore mocks before or after each test run to undo mock state changes between runs! See mockReset docs for more info.

If you are not familiar with vi.fn, vi.mock or vi.spyOn methods, check the API section first.

Dates
Sometimes you need to be in control of the date to ensure consistency when testing. Vitest uses @sinonjs/fake-timers package for manipulating timers, as well as system date. You can find more about the specific API in detail here.

Example

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'

const businessHours = [9, 17]

function purchase() {
  const currentHour = new Date().getHours()
  const [open, close] = businessHours

  if (currentHour > open && currentHour < close) {
    return { message: 'Success' }
  }

  return { message: 'Error' }
}

describe('purchasing flow', () => {
  beforeEach(() => {
    // tell vitest we use mocked time
    vi.useFakeTimers()
  })

  afterEach(() => {
    // restoring date after each test run
    vi.useRealTimers()
  })

  it('allows purchases within business hours', () => {
    // set hour within business hours
    const date = new Date(2000, 1, 1, 13)
    vi.setSystemTime(date)

    // access Date.now() will result in the date set above
    expect(purchase()).toEqual({ message: 'Success' })
  })

  it('disallows purchases outside of business hours', () => {
    // set hour outside business hours
    const date = new Date(2000, 1, 1, 19)
    vi.setSystemTime(date)

    // access Date.now() will result in the date set above
    expect(purchase()).toEqual({ message: 'Error' })
  })
})
Functions
Mocking functions can be split up into two different categories; spying & mocking.

Sometimes all you need is to validate whether or not a specific function has been called (and possibly which arguments were passed). In these cases a spy would be all we need which you can use directly with vi.spyOn() (read more here).

However spies can only help you spy on functions, they are not able to alter the implementation of those functions. In the case where we do need to create a fake (or mocked) version of a function we can use vi.fn() (read more here).

We use Tinyspy as a base for mocking functions, but we have our own wrapper to make it jest compatible. Both vi.fn() and vi.spyOn() share the same methods, however only the return result of vi.fn() is callable.

Example

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

const messages = {
  items: [
    { message: 'Simple test message', from: 'Testman' },
    // ...
  ],
  getLatest, // can also be a `getter or setter if supported`
}

function getLatest(index = messages.items.length - 1) {
  return messages.items[index]
}

describe('reading messages', () => {
  afterEach(() => {
    vi.restoreAllMocks()
  })

  it('should get the latest message with a spy', () => {
    const spy = vi.spyOn(messages, 'getLatest')
    expect(spy.getMockName()).toEqual('getLatest')

    expect(messages.getLatest()).toEqual(
      messages.items[messages.items.length - 1],
    )

    expect(spy).toHaveBeenCalledTimes(1)

    spy.mockImplementationOnce(() => 'access-restricted')
    expect(messages.getLatest()).toEqual('access-restricted')

    expect(spy).toHaveBeenCalledTimes(2)
  })

  it('should get with a mock', () => {
    const mock = vi.fn().mockImplementation(getLatest)

    expect(mock()).toEqual(messages.items[messages.items.length - 1])
    expect(mock).toHaveBeenCalledTimes(1)

    mock.mockImplementationOnce(() => 'access-restricted')
    expect(mock()).toEqual('access-restricted')

    expect(mock).toHaveBeenCalledTimes(2)

    expect(mock()).toEqual(messages.items[messages.items.length - 1])
    expect(mock).toHaveBeenCalledTimes(3)
  })
})
More
Jest's Mock Functions
Globals
You can mock global variables that are not present with jsdom or node by using vi.stubGlobal helper. It will put the value of the global variable into a globalThis object.


import { vi } from 'vitest'

const IntersectionObserverMock = vi.fn(() => ({
  disconnect: vi.fn(),
  observe: vi.fn(),
  takeRecords: vi.fn(),
  unobserve: vi.fn(),
}))

vi.stubGlobal('IntersectionObserver', IntersectionObserverMock)

// now you can access it as `IntersectionObserver` or `window.IntersectionObserver`
Modules
Mock modules observe third-party-libraries, that are invoked in some other code, allowing you to test arguments, output or even redeclare its implementation.

See the vi.mock() API section for a more in-depth detailed API description.

Automocking Algorithm
If your code is importing a mocked module, without any associated __mocks__ file or factory for this module, Vitest will mock the module itself by invoking it and mocking every export.

The following principles apply

All arrays will be emptied
All primitives and collections will stay the same
All objects will be deeply cloned
All instances of classes and their prototypes will be deeply cloned
Virtual Modules
Vitest supports mocking Vite virtual modules. It works differently from how virtual modules are treated in Jest. Instead of passing down virtual: true to a vi.mock function, you need to tell Vite that module exists otherwise it will fail during parsing. You can do that in several ways:

Provide an alias
vitest.config.js

import { defineConfig } from 'vitest/config'
import { resolve } from 'node:path'
export default defineConfig({
  test: {
    alias: {
      '$app/forms': resolve('./mocks/forms.js'),
    },
  },
})
Provide a plugin that resolves a virtual module
vitest.config.js

import { defineConfig } from 'vitest/config'
export default defineConfig({
  plugins: [
    {
      name: 'virtual-modules',
      resolveId(id) {
        if (id === '$app/forms') {
          return 'virtual:$app/forms'
        }
      },
    },
  ],
})
The benefit of the second approach is that you can dynamically create different virtual entrypoints. If you redirect several virtual modules into a single file, then all of them will be affected by vi.mock, so make sure to use unique identifiers.

Mocking Pitfalls
Beware that it is not possible to mock calls to methods that are called inside other methods of the same file. For example, in this code:

foobar.js

export function foo() {
  return 'foo'
}

export function foobar() {
  return `${foo()}bar`
}
It is not possible to mock the foo method from the outside because it is referenced directly. So this code will have no effect on the foo call inside foobar (but it will affect the foo call in other modules):

foobar.test.ts

import { vi } from 'vitest'
import * as mod from './foobar.js'

// this will only affect "foo" outside of the original module
vi.spyOn(mod, 'foo')
vi.mock('./foobar.js', async (importOriginal) => {
  return {
    ...await importOriginal<typeof import('./foobar.js')>(),
    // this will only affect "foo" outside of the original module
    foo: () => 'mocked'
  }
})
You can confirm this behaviour by providing the implementation to the foobar method directly:

foobar.test.js

import * as mod from './foobar.js'

vi.spyOn(mod, 'foo')

// exported foo references mocked method
mod.foobar(mod.foo)
foobar.js

export function foo() {
  return 'foo'
}

export function foobar(injectedFoo) {
  return injectedFoo === foo // false
}
This is the intended behaviour. It is usually a sign of bad code when mocking is involved in such a manner. Consider refactoring your code into multiple files or improving your application architecture by using techniques such as dependency injection.

Example

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { Client } from 'pg'
import { failure, success } from './handlers.js'

// get todos
export async function getTodos(event, context) {
  const client = new Client({
    // ...clientOptions
  })

  await client.connect()

  try {
    const result = await client.query('SELECT * FROM todos;')

    client.end()

    return success({
      message: `${result.rowCount} item(s) returned`,
      data: result.rows,
      status: true,
    })
  }
  catch (e) {
    console.error(e.stack)

    client.end()

    return failure({ message: e, status: false })
  }
}

vi.mock('pg', () => {
  const Client = vi.fn()
  Client.prototype.connect = vi.fn()
  Client.prototype.query = vi.fn()
  Client.prototype.end = vi.fn()

  return { Client }
})

vi.mock('./handlers.js', () => {
  return {
    success: vi.fn(),
    failure: vi.fn(),
  }
})

describe('get a list of todo items', () => {
  let client

  beforeEach(() => {
    client = new Client()
  })

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

  it('should return items successfully', async () => {
    client.query.mockResolvedValueOnce({ rows: [], rowCount: 0 })

    await getTodos()

    expect(client.connect).toBeCalledTimes(1)
    expect(client.query).toBeCalledWith('SELECT * FROM todos;')
    expect(client.end).toBeCalledTimes(1)

    expect(success).toBeCalledWith({
      message: '0 item(s) returned',
      data: [],
      status: true,
    })
  })

  it('should throw an error', async () => {
    const mError = new Error('Unable to retrieve rows')
    client.query.mockRejectedValueOnce(mError)

    await getTodos()

    expect(client.connect).toBeCalledTimes(1)
    expect(client.query).toBeCalledWith('SELECT * FROM todos;')
    expect(client.end).toBeCalledTimes(1)
    expect(failure).toBeCalledWith({ message: mError, status: false })
  })
})
File System
Mocking the file system ensures that the tests do not depend on the actual file system, making the tests more reliable and predictable. This isolation helps in avoiding side effects from previous tests. It allows for testing error conditions and edge cases that might be difficult or impossible to replicate with an actual file system, such as permission issues, disk full scenarios, or read/write errors.

Vitest doesn't provide any file system mocking API out of the box. You can use vi.mock to mock the fs module manually, but it's hard to maintain. Instead, we recommend using memfs to do that for you. memfs creates an in-memory file system, which simulates file system operations without touching the actual disk. This approach is fast and safe, avoiding any potential side effects on the real file system.

Example
To automatically redirect every fs call to memfs, you can create __mocks__/fs.cjs and __mocks__/fs/promises.cjs files at the root of your project:


__mocks__/fs.cjs

__mocks__/fs/promises.cjs

// we can also use `import`, but then
// every export should be explicitly defined

const { fs } = require('memfs')
module.exports = fs
read-hello-world.js

import { readFileSync } from 'node:fs'

export function readHelloWorld(path) {
  return readFileSync(path, 'utf-8')
}
hello-world.test.js

import { beforeEach, expect, it, vi } from 'vitest'
import { fs, vol } from 'memfs'
import { readHelloWorld } from './read-hello-world.js'

// tell vitest to use fs mock from __mocks__ folder
// this can be done in a setup file if fs should always be mocked
vi.mock('node:fs')
vi.mock('node:fs/promises')

beforeEach(() => {
  // reset the state of in-memory fs
  vol.reset()
})

it('should return correct text', () => {
  const path = '/hello-world.txt'
  fs.writeFileSync(path, 'hello world')

  const text = readHelloWorld(path)
  expect(text).toBe('hello world')
})

it('can return a value multiple times', () => {
  // you can use vol.fromJSON to define several files
  vol.fromJSON(
    {
      './dir1/hw.txt': 'hello dir1',
      './dir2/hw.txt': 'hello dir2',
    },
    // default cwd
    '/tmp',
  )

  expect(readHelloWorld('/tmp/dir1/hw.txt')).toBe('hello dir1')
  expect(readHelloWorld('/tmp/dir2/hw.txt')).toBe('hello dir2')
})
Requests
Because Vitest runs in Node, mocking network requests is tricky; web APIs are not available, so we need something that will mimic network behavior for us. We recommend Mock Service Worker to accomplish this. It allows you to mock http, WebSocket and GraphQL network requests, and is framework agnostic.

Mock Service Worker (MSW) works by intercepting the requests your tests make, allowing you to use it without changing any of your application code. In-browser, this uses the Service Worker API. In Node.js, and for Vitest, it uses the @mswjs/interceptors library. To learn more about MSW, read their introduction

Configuration
You can use it like below in your setup file


HTTP Setup

GraphQL Setup

WebSocket Setup

import { afterAll, afterEach, beforeAll } from 'vitest'
import { setupServer } from 'msw/node'
import { http, HttpResponse } from 'msw'

const posts = [
  {
    userId: 1,
    id: 1,
    title: 'first post title',
    body: 'first post body',
  },
  // ...
]

export const restHandlers = [
  http.get('https://rest-endpoint.example/path/to/posts', () => {
    return HttpResponse.json(posts)
  }),
]

const server = setupServer(...restHandlers)

// Start server before all tests
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))

// Close server after all tests
afterAll(() => server.close())

// Reset handlers after each test for test isolation
afterEach(() => server.resetHandlers())
Configuring the server with onUnhandledRequest: 'error' ensures that an error is thrown whenever there is a request that does not have a corresponding request handler.

More
There is much more to MSW. You can access cookies and query parameters, define mock error responses, and much more! To see all you can do with MSW, read their documentation.

Timers
When we test code that involves timeouts or intervals, instead of having our tests wait it out or timeout, we can speed up our tests by using "fake" timers that mock calls to setTimeout and setInterval.

See the vi.useFakeTimers API section for a more in depth detailed API description.

Example

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'

function executeAfterTwoHours(func) {
  setTimeout(func, 1000 * 60 * 60 * 2) // 2 hours
}

function executeEveryMinute(func) {
  setInterval(func, 1000 * 60) // 1 minute
}

const mock = vi.fn(() => console.log('executed'))

describe('delayed execution', () => {
  beforeEach(() => {
    vi.useFakeTimers()
  })
  afterEach(() => {
    vi.restoreAllMocks()
  })
  it('should execute the function', () => {
    executeAfterTwoHours(mock)
    vi.runAllTimers()
    expect(mock).toHaveBeenCalledTimes(1)
  })
  it('should not execute the function', () => {
    executeAfterTwoHours(mock)
    // advancing by 2ms won't trigger the func
    vi.advanceTimersByTime(2)
    expect(mock).not.toHaveBeenCalled()
  })
  it('should execute every minute', () => {
    executeEveryMinute(mock)
    vi.advanceTimersToNextTimer()
    expect(mock).toHaveBeenCalledTimes(1)
    vi.advanceTimersToNextTimer()
    expect(mock).toHaveBeenCalledTimes(2)
  })
})
Classes
You can mock an entire class with a single vi.fn call - since all classes are also functions, this works out of the box. Beware that currently Vitest doesn't respect the new keyword so the new.target is always undefined in the body of a function.


class Dog {
  name: string

  constructor(name: string) {
    this.name = name
  }

  static getType(): string {
    return 'animal'
  }

  greet = (): string => {
    return `Hi! My name is ${this.name}!`
  }

  speak(): string {
    return 'bark!'
  }

  isHungry() {}
  feed() {}
}
We can re-create this class with ES5 functions:


const Dog = vi.fn(function (name) {
  this.name = name
  // mock instance methods in the constructor, each instance will have its own spy
  this.greet = vi.fn(() => `Hi! My name is ${this.name}!`)
})

// notice that static methods are mocked directly on the function,
// not on the instance of the class
Dog.getType = vi.fn(() => 'mocked animal')

// mock the "speak" and "feed" methods on every instance of a class
// all `new Dog()` instances will inherit and share these spies
Dog.prototype.speak = vi.fn(() => 'loud bark!')
Dog.prototype.feed = vi.fn()
WARNING

If a non-primitive is returned from the constructor function, that value will become the result of the new expression. In this case the [[Prototype]] may not be correctly bound:


const CorrectDogClass = vi.fn(function (name) {
  this.name = name
})

const IncorrectDogClass = vi.fn(name => ({
  name
}))

const Marti = new CorrectDogClass('Marti')
const Newt = new IncorrectDogClass('Newt')

Marti instanceof CorrectDogClass // ✅ true
Newt instanceof IncorrectDogClass // ❌ false!
WHEN TO USE?

Generally speaking, you would re-create a class like this inside the module factory if the class is re-exported from another module:


import { Dog } from './dog.js'

vi.mock(import('./dog.js'), () => {
  const Dog = vi.fn()
  Dog.prototype.feed = vi.fn()
  // ... other mocks
  return { Dog }
})
This method can also be used to pass an instance of a class to a function that accepts the same interface:

src/feed.ts

function feed(dog: Dog) {
  // ...
}
tests/dog.test.ts

import { expect, test, vi } from 'vitest'
import { feed } from '../src/feed.js'

const Dog = vi.fn()
Dog.prototype.feed = vi.fn()

test('can feed dogs', () => {
  const dogMax = new Dog('Max')

  feed(dogMax)

  expect(dogMax.feed).toHaveBeenCalled()
  expect(dogMax.isHungry()).toBe(false)
})
Now, when we create a new instance of the Dog class its speak method (alongside feed and greet) is already mocked:


const Cooper = new Dog('Cooper')
Cooper.speak() // loud bark!
Cooper.greet() // Hi! My name is Cooper!

// you can use built-in assertions to check the validity of the call
expect(Cooper.speak).toHaveBeenCalled()
expect(Cooper.greet).toHaveBeenCalled()

const Max = new Dog('Max')

// methods assigned to the prototype are shared between instances
expect(Max.speak).toHaveBeenCalled()
expect(Max.greet).not.toHaveBeenCalled()
We can reassign the return value for a specific instance:


const dog = new Dog('Cooper')

// "vi.mocked" is a type helper, since
// TypeScript doesn't know that Dog is a mocked class,
// it wraps any function in a MockInstance<T> type
// without validating if the function is a mock
vi.mocked(dog.speak).mockReturnValue('woof woof')

dog.speak() // woof woof
To mock the property, we can use the vi.spyOn(dog, 'name', 'get') method. This makes it possible to use spy assertions on the mocked property:


const dog = new Dog('Cooper')

const nameSpy = vi.spyOn(dog, 'name', 'get').mockReturnValue('Max')

expect(dog.name).toBe('Max')
expect(nameSpy).toHaveBeenCalledTimes(1)
TIP

You can also spy on getters and setters using the same method.

Cheat Sheet
INFO

vi in the examples below is imported directly from vitest. You can also use it globally, if you set globals to true in your config.

I want to…

Mock exported variables
example.js

export const getter = 'variable'
example.test.ts

import * as exports from './example.js'

vi.spyOn(exports, 'getter', 'get').mockReturnValue('mocked')
Mock an exported function
Example with vi.mock:
WARNING

Don't forget that a vi.mock call is hoisted to top of the file. It will always be executed before all imports.

example.js

export function method() {}

import { method } from './example.js'

vi.mock('./example.js', () => ({
  method: vi.fn()
}))
Example with vi.spyOn:

import * as exports from './example.js'

vi.spyOn(exports, 'method').mockImplementation(() => {})
Mock an exported class implementation
Example with vi.mock and .prototype:
example.js

export class SomeClass {}

import { SomeClass } from './example.js'

vi.mock(import('./example.js'), () => {
  const SomeClass = vi.fn()
  SomeClass.prototype.someMethod = vi.fn()
  return { SomeClass }
})
// SomeClass.mock.instances will have SomeClass
Example with vi.spyOn:

import * as mod from './example.js'

const SomeClass = vi.fn()
SomeClass.prototype.someMethod = vi.fn()

vi.spyOn(mod, 'SomeClass').mockImplementation(SomeClass)
Spy on an object returned from a function
Example using cache:
example.js

export function useObject() {
  return { method: () => true }
}
useObject.js

import { useObject } from './example.js'

const obj = useObject()
obj.method()
useObject.test.js

import { useObject } from './example.js'

vi.mock(import('./example.js'), () => {
  let _cache
  const useObject = () => {
    if (!_cache) {
      _cache = {
        method: vi.fn(),
      }
    }
    // now every time that useObject() is called it will
    // return the same object reference
    return _cache
  }
  return { useObject }
})

const obj = useObject()
// obj.method was called inside some-path
expect(obj.method).toHaveBeenCalled()
Mock part of a module

import { mocked, original } from './some-path.js'

vi.mock(import('./some-path.js'), async (importOriginal) => {
  const mod = await importOriginal()
  return {
    ...mod,
    mocked: vi.fn()
  }
})
original() // has original behaviour
mocked() // is a spy function
WARNING

Don't forget that this only mocks external access. In this example, if original calls mocked internally, it will always call the function defined in the module, not in the mock factory.

Mock the current date
To mock Date's time, you can use vi.setSystemTime helper function. This value will not automatically reset between different tests.

Beware that using vi.useFakeTimers also changes the Date's time.


const mockDate = new Date(2022, 0, 1)
vi.setSystemTime(mockDate)
const now = new Date()
expect(now.valueOf()).toBe(mockDate.valueOf())
// reset mocked time
vi.useRealTimers()
Mock a global variable
You can set global variable by assigning a value to globalThis or using vi.stubGlobal helper. When using vi.stubGlobal, it will not automatically reset between different tests, unless you enable unstubGlobals config option or call vi.unstubAllGlobals.


vi.stubGlobal('__VERSION__', '1.0.0')
expect(__VERSION__).toBe('1.0.0')
Mock import.meta.env
To change environmental variable, you can just assign a new value to it.
WARNING

The environmental variable value will not automatically reset between different tests.


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

// you can reset it in beforeEach hook manually
const originalViteEnv = import.meta.env.VITE_ENV

beforeEach(() => {
  import.meta.env.VITE_ENV = originalViteEnv
})

it('changes value', () => {
  import.meta.env.VITE_ENV = 'staging'
  expect(import.meta.env.VITE_ENV).toBe('staging')
})
If you want to automatically reset the value(s), you can use the vi.stubEnv helper with the unstubEnvs config option enabled (or call vi.unstubAllEnvs manually in a beforeEach hook):

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

// before running tests "VITE_ENV" is "test"
import.meta.env.VITE_ENV === 'test'

it('changes value', () => {
  vi.stubEnv('VITE_ENV', 'staging')
  expect(import.meta.env.VITE_ENV).toBe('staging')
})

it('the value is restored before running an other test', () => {
  expect(import.meta.env.VITE_ENV).toBe('test')
})
vitest.config.ts

export default defineConfig({
  test: {
    unstubEnvs: true,
  },
})