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.
Vitest has a comprehensive list of guides regarding mocking:
- Mocking Classes
- Mocking Dates
- Mocking the File System
- Mocking Functions
- Mocking Globals
- Mocking Modules
- Mocking Requests
- Mocking Timers
For a simpler and quicker way to get started with mocking, you can check the Cheat Sheet below.
Cheat Sheet
I want to…
Mock exported variables
export const getter = 'variable'
import * as exports from './example.js'
vi.spyOn(exports, 'getter', 'get').mockReturnValue('mocked')
WARNING
This will not work in the Browser Mode. For a workaround, see Limitations.
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.
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(() => {})
WARNING
vi.spyOn
example will not work in the Browser Mode. For a workaround, see Limitations.
Mock an exported class implementation
- Example with a fake
class
:
export class SomeClass {}
import { SomeClass } from './example.js'
vi.mock(import('./example.js'), () => {
const SomeClass = vi.fn(class FakeClass {
someMethod = vi.fn()
})
return { SomeClass }
})
- Example with
vi.spyOn
:
import * as mod from './example.js'
vi.spyOn(mod, 'SomeClass').mockImplementation(class FakeClass {
someMethod = vi.fn()
})
WARNING
vi.spyOn
example will not work in the Browser Mode. For a workaround, see Limitations.
Spy on an object returned from a function
- Example using cache:
export function useObject() {
return { method: () => true }
}
import { useObject } from './example.js'
const obj = useObject()
obj.method()
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 theunstubEnvs
config option enabled (or callvi.unstubAllEnvs
manually in abeforeEach
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')
})
export default defineConfig({
test: {
unstubEnvs: true,
},
})