Mock Functions
你可以使用 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
中使用间谍断言来断言 spy 的结果。此 API 参考描述了可用于操作 spy 行为的可用属性和方法。
getMockName
类型:
() => string
返回通过
.mockName(name)
方法设置的模拟名称。
mockClear
类型:
() => MockInstance
清除关于每次调用的所有信息。调用该方法后,
spy.mock.calls
和spy.mock.results
将返回空数组。这在需要在不同断言之间清理 spy 时非常有用。如果你希望在每次测试之前自动调用此方法,你可以在配置中启用
clearMocks
设置。
mockName
类型:
(name: string) => MockInstance
设置内部模拟名称。便于在断言失败时识别是哪个模拟出了问题。
mockImplementation
类型:
(fn: Function) => MockInstance
接受一个函数,该函数将用作模拟的实现。
例如:
tsconst 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
接受一个函数,该函数将作为模拟函数的单次调用实现。可以链式调用,以使多次函数调用产生不同的结果。
tsconst myMockFn = vi .fn() .mockImplementationOnce(() => true) .mockImplementationOnce(() => false); myMockFn(); // true myMockFn(); // false
当模拟函数的单次调用实现用尽时,它将调用使用
vi.fn(() => defaultValue)
或.mockImplementation(() => defaultValue)
设置的默认实现(如果已设置)。tsconst 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>
在回调函数执行期间,临时覆盖原始模拟实现。
jsconst myMockFn = vi.fn(() => 'original'); myMockFn.withImplementation( () => 'temp', () => { myMockFn(); // 'temp' } ); myMockFn(); // 'original'
可以与异步回调一起使用。需要等待该方法执行完成后才能继续使用原始实现。
tstest('async callback', () => { const myMockFn = vi.fn(() => 'original'); // 因为回调是异步的,所以我们需要 await 这个调用 await myMockFn.withImplementation( () => 'temp', async () => { myMockFn(); // 'temp' } ); myMockFn(); // 'original' });
此外,它优先于
mockImplementationOnce
。
mockRejectedValue
类型:
(value: any) => MockInstance
接受一个值,当调用异步函数时,返回一个 rejected 状态的 Promise,并将该值作为拒绝原因。
tsconst asyncMock = vi.fn().mockRejectedValue(new Error('Async error')); await asyncMock(); // throws "Async error"
mockRejectedValueOnce
类型:
(value: any) => MockInstance
接受一个值,该值将导致模拟函数的单次调用返回一个 rejected 状态的 Promise。如果链式调用,则后续每次调用都将以传递的值作为拒绝原因。
tsconst asyncMock = vi .fn() .mockResolvedValueOnce('first call') .mockRejectedValueOnce(new Error('Async error')); await asyncMock(); // first call await asyncMock(); // throws "Async error"
mockReset
类型:
() => MockInstance
执行与
mockClear
相同的操作,并将内部实现设置为空函数(调用时返回undefined
)。当你需要将模拟完全重置回初始状态时,这非常有用。如果你希望在每次测试之前自动调用此方法,你可以在配置中启用
mockReset
设置。
mockRestore
类型:
() => MockInstance
执行与
mockReset
相同的操作,并将内部实现恢复为原始函数。请注意,从
vi.fn()
恢复 mock 会将实现设置为空函数,该函数返回undefined
。恢复vi.fn(impl)
会将实现恢复为impl
。如果你希望在每次测试之前自动调用此方法,你可以在配置中启用
restoreMocks
设置。
mockResolvedValue
类型:
(value: any) => MockInstance
接受一个值,当调用异步函数时,返回一个 resolved 状态的 Promise,并将该值作为解析结果。
tsconst asyncMock = vi.fn().mockResolvedValue(43); await asyncMock(); // 43
mockResolvedValueOnce
类型:
(value: any) => MockInstance
接受一个值,该值将作为模拟函数的单次调用解析结果。如果链式调用,则后续每次调用都将以传递的值作为解析结果。
tsconst 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
上下文。
mockReturnValue
类型:
(value: any) => MockInstance
接受一个值,该值将在每次调用模拟函数时被返回。
tsconst mock = vi.fn(); mock.mockReturnValue(42); mock(); // 42 mock.mockReturnValue(43); mock(); // 43
mockReturnValueOnce
类型:
(value: any) => MockInstance
接受一个值,该值将作为模拟函数的单次调用返回值。如果链式调用,则后续每次调用都将返回传递的值。当 mockReturnValueOnce 的值用尽时,将调用由 mockImplementation 或其他 mockReturn* 方法指定的函数。
tsconst 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', 'arg4');
fn.mock.calls ===
[
['arg1', 'arg2'], // first call
['arg3', 'arg4'], // second call
];
mock.lastCall
这里存储着上次调用时传递给模拟函数的参数。如果 spy 未被调用,将返回 undefined
。
mock.results
这是一个数组,其中包含函数每次调用的结果。数组中的每个元素都是一个对象,包含 type
和 value
属性。可用的类型有:
'return'
- 函数正常返回。'throw'
- 函数抛出了一个值。
value
属性包含返回值或抛出的错误。如果函数返回一个 Promise,当 Promise 被 resolve 后,value 属性将变为 Promise 的 resolve 值。
const fn = vi.fn();
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.instances
这是一个数组,其中包含通过 new
关键字调用 mock 函数时创建的所有实例。请注意,这是函数的实际上下文(this
),而不是返回值。
WARNING
如果使用 new MyClass()
实例化 mock,则 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;