vitest/docs/api/mock.md

601 lines
15 KiB
Markdown

# Mocks
You can create a mock function or a class to track its execution with the `vi.fn` method. If you want to track a property on an already created object, you can use the `vi.spyOn` method:
```js
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`](/api/expect#tohavebeencalled)) on [`expect`](/api/expect) to assert mock results. 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
```ts
function getMockImplementation(): T | undefined
```
Returns the current mock implementation if there is one.
If the mock was created with [`vi.fn`](/api/vi#vi-fn), it will use the provided method as the mock implementation.
If the mock was created with [`vi.spyOn`](/api/vi#vi-spyon), it will return `undefined` unless a custom implementation is provided.
## getMockName
```ts
function getMockName(): string
```
Use it to return the name assigned to the mock with the `.mockName(name)` method. By default, `vi.fn()` mocks will return `'vi.fn()'`, while spies created with `vi.spyOn` will keep the original name.
## mockClear
```ts
function mockClear(): Mock<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.
```ts
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`](/config/#clearmocks) setting in the configuration.
## mockName
```ts
function mockName(name: string): Mock<T>
```
Sets the internal mock name. This is useful for identifying the mock when an assertion fails.
## mockImplementation
```ts
function mockImplementation(fn: T): Mock<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.
```ts
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
```ts
function mockImplementationOnce(fn: T): Mock<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.
```ts
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:
```ts
const myMockFn = vi
.fn(() => 'default')
.mockImplementationOnce(() => 'first call')
.mockImplementationOnce(() => 'second call')
// 'first call', 'second call', 'default', 'default'
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn())
```
## withImplementation
```ts
function withImplementation(
fn: T,
cb: () => void
): Mock<T>
function withImplementation(
fn: T,
cb: () => Promise<void>
): Promise<Mock<T>>
```
Overrides the original mock implementation temporarily while the callback is being executed.
```js
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.
```ts
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`](#mockimplementationonce).
## mockRejectedValue
```ts
function mockRejectedValue(value: unknown): Mock<T>
```
Accepts an error that will be rejected when an async function is called.
```ts
const asyncMock = vi.fn().mockRejectedValue(new Error('Async error'))
await asyncMock() // throws Error<'Async error'>
```
## mockRejectedValueOnce
```ts
function mockRejectedValueOnce(value: unknown): Mock<T>
```
Accepts a value that will be rejected during the next function call. If chained, each consecutive call will reject the specified value.
```ts
const asyncMock = vi
.fn()
.mockResolvedValueOnce('first call')
.mockRejectedValueOnce(new Error('Async error'))
await asyncMock() // 'first call'
await asyncMock() // throws Error<'Async error'>
```
## mockReset
```ts
function mockReset(): Mock<T>
```
Does what [`mockClear`](#mockClear) does and resets the mock implementation. This also resets all "once" implementations.
Note that resetting a mock from `vi.fn()` will set the implementation to an empty function that returns `undefined`.
Resetting a mock from `vi.fn(impl)` will reset the implementation to `impl`.
This is useful when you want to reset a mock to its original state.
```ts
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`](/config/#mockreset) setting in the configuration.
## mockRestore
```ts
function mockRestore(): Mock<T>
```
Does what [`mockReset`](#mockreset) does and restores the original descriptors of spied-on objects, if the mock was created with [`vi.spyOn`](/api/vi#vi-spyon).
`mockRestore` on a `vi.fn()` mock is identical to [`mockReset`](#mockreset).
```ts
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`](/config/#restoremocks) setting in the configuration.
## mockResolvedValue
```ts
function mockResolvedValue(value: Awaited<ReturnType<T>>): Mock<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.
```ts
const asyncMock = vi.fn().mockResolvedValue(42)
await asyncMock() // 42
```
## mockResolvedValueOnce
```ts
function mockResolvedValueOnce(value: Awaited<ReturnType<T>>): Mock<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.
```ts
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
```ts
function mockReturnThis(): Mock<T>
```
Use this if you need to return the `this` context from the method without invoking the actual implementation. This is a shorthand for:
```ts
spy.mockImplementation(function () {
return this
})
```
## mockReturnValue
```ts
function mockReturnValue(value: ReturnType<T>): Mock<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.
```ts
const mock = vi.fn()
mock.mockReturnValue(42)
mock() // 42
mock.mockReturnValue(43)
mock() // 43
```
## mockReturnValueOnce
```ts
function mockReturnValueOnce(value: ReturnType<T>): Mock<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:
```ts
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
```ts
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.
```js
const fn = vi.fn()
fn('arg1', 'arg2')
fn('arg3')
fn.mock.calls === [
['arg1', 'arg2'], // first call
['arg3'], // second call
]
```
:::warning Objects are Stored by Reference
Note that Vitest always stores objects by reference in all properies of the `mock` state. This means that if the properties were changed by your code, then some assertions like [`.toHaveBeenCalledWith`](/api/expect#tohavebeencalledwith) will not pass:
```ts
const argument = {
value: 0,
}
const fn = vi.fn()
fn(argument) // { value: 0 }
argument.value = 10
expect(fn).toHaveBeenCalledWith({ value: 0 }) // [!code --]
// The equality check is done against the original argument,
// but its property was changed between the call and assertion
expect(fn).toHaveBeenCalledWith({ value: 10 }) // [!code ++]
```
In this case you can clone the argument yourself:
```ts{6}
const calledArguments = []
const fn = vi.fn((arg) => {
calledArguments.push(structuredClone(arg))
})
expect(calledArguments[0]).toEqual({ value: 0 })
```
:::
## mock.lastCall
```ts
const lastCall: Parameters<T> | undefined
```
This contains the arguments of the last call. If the mock wasn't called, it will return `undefined`.
## mock.results
```ts
interface MockResultReturn<T> {
type: 'return'
/**
* The value that was returned from the function.
* If the 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.
- `'incomplete'` - the function did not finish running yet.
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.
```js
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
```ts
interface MockSettledResultIncomplete {
type: 'incomplete'
value: undefined
}
interface MockSettledResultFulfilled<T> {
type: 'fulfilled'
value: T
}
interface MockSettledResultRejected {
type: 'rejected'
value: any
}
export type MockSettledResult<T>
= | MockSettledResultFulfilled<T>
| MockSettledResultRejected
| MockSettledResultIncomplete
const settledResults: MockSettledResult<Awaited<ReturnType<T>>>[]
```
An array containing all values that were resolved or rejected by the function.
If the function returned non-promise values, the `value` will be kept as is, but the `type` will still says `fulfilled` or `rejected`.
Until the value is resolved or rejected, the `settledResult` type will be `incomplete`.
```js
const fn = vi.fn().mockResolvedValueOnce('result')
const result = fn()
fn.mock.settledResults === [
{
type: 'incomplete',
value: undefined,
},
]
await result
fn.mock.settledResults === [
{
type: 'fulfilled',
value: 'result',
},
]
```
## mock.invocationCallOrder
```ts
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.
```js
const fn1 = vi.fn()
const fn2 = vi.fn()
fn1()
fn2()
fn1()
fn1.mock.invocationCallOrder === [1, 3]
fn2.mock.invocationCallOrder === [2]
```
## mock.contexts
```ts
const contexts: ThisParameterType<T>[]
```
This property is an array of `this` values used during each call to the mock function.
```js
const fn = vi.fn()
const context = {}
fn.apply(context)
fn.call(context)
fn.mock.contexts[0] === context
fn.mock.contexts[1] === context
```
## mock.instances
```ts
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 the actual context (`this`) of the function, not a return value.
::: warning
If the mock was instantiated with `new MyClass()`, then `mock.instances` will be an array with one value:
```js
const MyClass = vi.fn()
const a = new MyClass()
MyClass.mock.instances[0] === a
```
If you return a value from the constructor, it will not be in the `instances` array, but instead inside `results`:
```js
const Spy = vi.fn(() => ({ method: vi.fn() }))
const a = new Spy()
Spy.mock.instances[0] !== a
Spy.mock.results[0] === a
```
:::