vitest/docs/guide/mocking.md
2025-08-31 12:47:43 +02:00

241 lines
6.2 KiB
Markdown

---
title: Mocking | Guide
outline: false
---
# 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](/config/#globals) 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`](/api/mock#mockreset) docs for more info.
:::
If you are not familiar with `vi.fn`, `vi.mock` or `vi.spyOn` methods, check the [API section](/api/vi) first.
Vitest has a comprehensive list of guides regarding mocking:
- [Mocking Classes](/guide/mocking/classes.md)
- [Mocking Dates](/guide/mocking/dates.md)
- [Mocking the File System](/guide/mocking/file-system.md)
- [Mocking Functions](/guide/mocking/functions.md)
- [Mocking Globals](/guide/mocking/globals.md)
- [Mocking Modules](/guide/mocking/modules.md)
- [Mocking Requests](/guide/mocking/requests.md)
- [Mocking Timers](/guide/mocking/timers.md)
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
```js [example.js]
export const getter = 'variable'
```
```ts [example.test.ts]
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](/guide/browser/#spying-on-module-exports).
:::
### Mock an exported function
1. 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.
:::
```ts [example.js]
export function method() {}
```
```ts
import { method } from './example.js'
vi.mock('./example.js', () => ({
method: vi.fn()
}))
```
2. Example with `vi.spyOn`:
```ts
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](/guide/browser/#spying-on-module-exports).
:::
### Mock an exported class implementation
1. Example with a fake `class`:
```ts [example.js]
export class SomeClass {}
```
```ts
import { SomeClass } from './example.js'
vi.mock(import('./example.js'), () => {
const SomeClass = vi.fn(class FakeClass {
someMethod = vi.fn()
})
return { SomeClass }
})
```
2. Example with `vi.spyOn`:
```ts
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](/guide/browser/#spying-on-module-exports).
:::
### Spy on an object returned from a function
1. Example using cache:
```ts [example.js]
export function useObject() {
return { method: () => true }
}
```
```ts [useObject.js]
import { useObject } from './example.js'
const obj = useObject()
obj.method()
```
```ts [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
```ts
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](#mocking-pitfalls). 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.
```ts
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`](/api/vi#vi-stubglobal) helper. When using `vi.stubGlobal`, it will **not** automatically reset between different tests, unless you enable [`unstubGlobals`](/config/#unstubglobals) config option or call [`vi.unstubAllGlobals`](/api/vi#vi-unstuballglobals).
```ts
vi.stubGlobal('__VERSION__', '1.0.0')
expect(__VERSION__).toBe('1.0.0')
```
### Mock `import.meta.env`
1. 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.
:::
```ts
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')
})
```
2. If you want to automatically reset the value(s), you can use the `vi.stubEnv` helper with the [`unstubEnvs`](/config/#unstubenvs) config option enabled (or call [`vi.unstubAllEnvs`](/api/vi#vi-unstuballenvs) manually in a `beforeEach` hook):
```ts
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')
})
```
```ts [vitest.config.ts]
export default defineConfig({
test: {
unstubEnvs: true,
},
})
```