模拟函数
你可以使用 vi.fn
方法创建一个模拟函数来追踪其执行情况。如果你想追踪已创建对象上的方法,可以使用 vi.spyOn
方法:
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;
你应该在 expect
中使用模拟断言(例如,toHaveBeenCalled
)来断言模拟的结果。此 API 参考描述了可用于操作模拟行为的属性和方法。
getMockImplementation
- 类型:
(...args: any) => any
返回当前模拟的实现函数,如果存在的话。
如果模拟函数是通过 vi.fn
创建的,它会将传入的方法视为模拟实现。
如果模拟函数是通过 vi.spyOn
创建的,它将返回 undefined
,除非提供了自定义实现。
getMockName
- 类型:
() => string
返回使用 .mockName(name)
方法赋予模拟函数的名称。如果断言失败,这有助于查看模拟函数的名称,方便调试。
mockClear
- 类型:
() => MockInstance
清除每次调用模拟函数时记录的所有信息。调用该方法后,.mock
上的所有属性将返回空状态。此方法不会重置模拟函数的实现。如果你需要在不同的断言之间清理模拟函数,它会很有用。
如果你希望在每次测试之前自动调用此方法,你可以在配置中启用 clearMocks
设置。
mockName
- 类型:
(name: string) => MockInstance
设置模拟函数的内部名称。如果断言失败,可以使用此名称来帮助识别模拟函数。
mockImplementation
- 类型:
(fn: Function) => MockInstance
接受一个函数,该函数将用作模拟函数的实现。
import { vi } from 'vitest';
// ---cut---
const mockFn = vi.fn().mockImplementation(apples => 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
- 类型:
(fn: Function) => MockInstance
接受一个函数,该函数将在下一次调用时作为模拟函数的实现。支持链式调用,以便多次函数调用产生不同的结果。
import { vi } from 'vitest';
// ---cut---
const myMockFn = vi
.fn()
.mockImplementationOnce(() => true)
.mockImplementationOnce(() => false);
myMockFn(); // true
myMockFn(); // false
当模拟函数的临时实现全部使用完毕后,它将调用使用 vi.fn(() => defaultValue)
或 .mockImplementation(() => defaultValue)
设置的默认实现(如果已调用):
import { vi } from 'vitest';
// ---cut---
const myMockFn = vi
.fn(() => 'default')
.mockImplementationOnce(() => 'first call')
.mockImplementationOnce(() => 'second call');
// 'first call', 'second call', 'default', 'default'
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
withImplementation
- 类型:
(fn: Function, callback: () => void) => MockInstance
- 类型:
(fn: Function, callback: () => Promise<unknown>) => Promise<MockInstance>
在执行回调函数期间,临时覆盖原始的模拟函数实现。
import { vi } from 'vitest';
// ---cut---
const myMockFn = vi.fn(() => 'original');
myMockFn.withImplementation(
() => 'temp',
() => {
myMockFn(); // 'temp'
}
);
myMockFn(); // 'original'
可以与异步回调一起使用。必须等待此方法执行完毕,才能恢复使用原始实现。
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'
});
请注意,此方法优先于 mockImplementationOnce
。
mockRejectedValue
- 类型:
(value: any) => MockInstance
接受一个值,该值将在异步函数调用时被 Promise 拒绝(reject)。
import { vi } from 'vitest';
// ---cut---
const asyncMock = vi.fn().mockRejectedValue(new Error('Async error'));
await asyncMock(); // throws "Async error"
mockRejectedValueOnce
- 类型:
(value: any) => MockInstance
接受一个值,该值将在下一次函数调用期间被 Promise 拒绝(reject)。如果链式调用,则每次连续调用都将拒绝指定的值。
import { vi } from 'vitest';
// ---cut---
const asyncMock = vi
.fn()
.mockResolvedValueOnce('first call')
.mockRejectedValueOnce(new Error('Async error'));
await asyncMock(); // first call
await asyncMock(); // throws "Async error"
mockReset
- 类型:
() => MockInstance
执行与 mockClear
相同的操作,并将内部实现设置为空函数(调用后返回 undefined
)。这也会重置所有 "once" 实现。当你想要将模拟函数完全重置为默认状态时,这很有用。
如果你希望在每次测试之前自动调用此方法,你可以在配置中启用 mockReset
设置。
mockRestore
- 类型:
() => MockInstance
执行与 mockReset
相同的操作,并将内部实现恢复为原始函数。
请注意,从 vi.fn()
恢复模拟函数会将实现设置为返回 undefined
的空函数。恢复 vi.fn(impl)
会将实现恢复为 impl
。
如果你希望在每次测试之前自动调用此方法,你可以在配置中启用 restoreMocks
设置。
mockResolvedValue
- 类型:
(value: any) => MockInstance
接受一个值,该值将在调用异步函数时被 Promise 解析(resolve)。
import { vi } from 'vitest';
// ---cut---
const asyncMock = vi.fn().mockResolvedValue(42);
await asyncMock(); // 42
mockResolvedValueOnce
- 类型:
(value: any) => MockInstance
接受一个值,该值将在下一次函数调用期间被 Promise 解析(resolve)。如果链式调用,则每次连续调用都将解析指定的值。
import { vi } from 'vitest';
// ---cut---
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
- 类型:
() => MockInstance
如果需要在方法中返回 this
上下文,且不希望调用实际实现,请使用此方法。这是以下代码的简写:
spy.mockImplementation(function () {
return this;
});
mockReturnValue
- 类型:
(value: any) => MockInstance
接受一个值,该值将在每次调用模拟函数时返回。
import { vi } from 'vitest';
// ---cut---
const mock = vi.fn();
mock.mockReturnValue(42);
mock(); // 42
mock.mockReturnValue(43);
mock(); // 43
mockReturnValueOnce
- 类型:
(value: any) => MockInstance
接受一个值,该值将在下一次函数调用期间返回。如果链式调用,则每次连续调用都将返回指定的值。
当所有 mockReturnValueOnce
值都被使用后,如果存在先前定义的实现,模拟函数将回退到该实现。
import { vi } from 'vitest';
// ---cut---
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 fn = vi.fn();
fn('arg1', 'arg2');
fn('arg3');
fn.mock.calls ===
[
['arg1', 'arg2'], // first call
['arg3'], // second call
];
mock.lastCall
这包含最后一次调用的参数列表。如果未调用模拟函数,将返回 undefined
。
mock.results
这是一个数组,包含函数 返回
的所有结果。数组中的每个元素是一个具有属性 type
和 value
的对象。可用类型有:
'return'
- 函数正常返回,没有抛出异常。'throw'
- 函数抛出了一个异常。
value
属性包含返回值或抛出的错误。如果函数返回一个 Promise,则 value
将是已解析的值,而不是实际的 Promise 对象,除非该 Promise 从未被解析。
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.invocationCallOrder
模拟函数的执行顺序。这将返回一个数字数组,其中的数字代表所有已定义的模拟函数的调用顺序。
const fn1 = vi.fn();
const fn2 = vi.fn();
fn1();
fn2();
fn1();
fn1.mock.invocationCallOrder === [1, 3];
fn2.mock.invocationCallOrder === [2];
mock.instances
这是一个数组,包含在使用 new
关键字调用模拟函数时实例化的所有实例。请注意,这里指的是函数的实际上下文 (this
),而不是函数的返回值。
WARNING
如果使用 new MyClass()
实例化模拟函数,则 mock.instances
将是一个包含一个值的数组:
const MyClass = vi.fn();
const a = new MyClass();
MyClass.mock.instances[0] === a;
如果你从构造函数返回一个值,它将不会在 instances
数组中,而是在 results
中:
const Spy = vi.fn(() => ({ method: vi.fn() }));
const a = new Spy();
Spy.mock.instances[0] !== a;
Spy.mock.results[0] === a;