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?: (importOriginal: () => unknown) => unknown) => 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.
WARNING
The browser mode does not presently support mocking modules. You can track this feature in the GitHub issue.
If factory
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.
// when using JavaScript
.('./path/to/module.js', async () => {
const = await ()
return {
...,
// replace some exports
: .(),
}
})
// when using TypeScript
vi.mock('./path/to/module.js', async (importOriginal) => {
const mod = await importOriginal<typeof import('./path/to/module.js')>()
return {
...mod,
// replace some exports
namedExport: vi.fn(),
}
})
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 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?: (importOriginal: () => unknown) => unknown) => 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
}
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.
import example from './example.js'
vi.mock('./example.js')
test('1 + 1 equals 10', async () => {
vi.mocked(example.calc).mockReturnValue(10)
expect(example.calc(1, '+', 1)).toBe(10)
})
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 axios = await vi.importActual('./example.js')
return { ...axios, 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) => 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) => 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
}
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 = .(() => 0)
()
().()
().(0)
.(5)
const = ()
().(5)
().(2, 5)
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
Will call .mockClear()
on all spies. This will clear mock history, but not reset its implementation to the default one.
vi.resetAllMocks
Will call .mockReset()
on all spies. This will clear mock history and reset its implementation to an empty function (will return undefined
).
vi.restoreAllMocks
Will call .mockRestore()
on all spies. This will clear mock history and reset its implementation to the original one.
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 = 0
const = {
: () => 42,
}
const = .(, 'getApples').(() => )
= 1
(.()).(1)
().()
().(1)
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!
vi.stubEnv 0.26.0+
- Type:
(name: string, value: string) => 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'
// 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 0.26.0+
- 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 { } from 'vitest'
// `innerWidth` is "0" before calling stubGlobal
.('innerWidth', 100)
=== 100
. === 100
// if you are using jsdom or happy-dom
. === 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 0.26.0+
- 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 descibes 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 = 0
(() => .(++), 50)
.(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 = 0
(() => .().(() => .(++)), 50)
await .(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 = 0
(() => .(++), 50)
.() // log: 1
.() // log: 2
.() // 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 = 0
(() => .().(() => .(++)), 50)
await .() // log: 1
(.).(1)
await .() // log: 2
await .() // log: 3
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 that was set using setSystemTime
. 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 = 0
(() => .(++))
const = (() => {
.(++)
if ( === 3)
()
}, 50)
.()
// 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
).
(async () => {
.(await .('result'))
}, 100)
await .()
// 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 = 0
(() => .(++), 50)
.()
// 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.
(() => {
.(1)
}, 100)
(() => {
.().(() => {
.(2)
(() => {
.(3)
}, 40)
})
}, 10)
await .()
// 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.
const = new (1998, 11, 19)
.()
.()
(.()).(.())
.()
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
Since version 0.35.0
vi.useFakeTimers()
no longer automatically mocks process.nextTick
. It can still be mocked by specifying the option in toFake
argument: vi.useFakeTimers({ toFake: ['nextTick'] })
.
vi.isFakeTimers 0.34.5+
- Type:
() => boolean
Returns true
if fake timers are enabled.
vi.useRealTimers
- Type:
() => Vitest
When timers are 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 0.34.5+
- 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.
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 0.34.5+
- 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 { , , } from 'vitest'
('Element render correctly', async () => {
const = await .(
() => .('.element'),
{
: 500, // default is 1000
: 20, // default is 50
}
)
// do something with the element
(.('.element-child')).()
})
vi.hoisted 0.31.0+
- 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.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
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 promised = 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.