模擬函式
您可以使用 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 參考描述了可用於操作模擬行為的屬性和方法。
TIP
以下類型中的自訂函式實作以泛型 <T>
標記。
getMockImplementation
function getMockImplementation(): T | undefined;
若存在,則傳回目前的模擬實作。
如果模擬是使用 vi.fn
建立的,它會使用提供的方法作為模擬實作。
如果模擬是使用 vi.spyOn
建立的,除非提供了自訂實作,否則它會傳回 undefined
。
getMockName
function getMockName(): string;
使用此方法傳回透過 .mockName(name)
方法分配給模擬的名稱。預設情況下,它會傳回 vi.fn()
。
mockClear
function mockClear(): MockInstance<T>;
清除每次呼叫的所有資訊。呼叫此方法後,.mock
上的所有屬性都將恢復到其初始狀態。此方法不會重置實作。這對於在不同斷言之間清除模擬很有用。
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']]);
若要在每次測試之前自動呼叫此方法,請在設定中啟用 clearMocks
。
mockName
function mockName(name: string): MockInstance<T>;
設定內部模擬名稱。這在斷言失敗時,有助於識別模擬。
mockImplementation
function mockImplementation(fn: T): MockInstance<T>;
接受一個函式作為模擬實作。TypeScript 期望其參數和傳回類型與原始函式相符。
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
function mockImplementationOnce(fn: T): MockInstance<T>;
接受一個函式作為模擬實作。TypeScript 期望其參數和傳回類型與原始函式相符。此方法可以串聯,以針對多次函式呼叫產生不同的結果。
const myMockFn = vi
.fn()
.mockImplementationOnce(() => true) // 1st call
.mockImplementationOnce(() => false); // 2nd call
myMockFn(); // 1st call: true
myMockFn(); // 2nd call: false
當模擬函式用完實作時,如果呼叫了 vi.fn(() => defaultValue)
或 .mockImplementation(() => defaultValue)
,它會呼叫預設實作:
const myMockFn = vi
.fn(() => 'default')
.mockImplementationOnce(() => 'first call')
.mockImplementationOnce(() => 'second call');
// 'first call', 'second call', 'default', 'default'
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
withImplementation
function withImplementation(fn: T, cb: () => void): MockInstance<T>;
function withImplementation(
fn: T,
cb: () => Promise<void>
): Promise<MockInstance<T>>;
在回呼執行期間,暫時覆寫原始模擬實作。
const myMockFn = vi.fn(() => 'original');
myMockFn.withImplementation(
() => 'temp',
() => {
myMockFn(); // 'temp'
}
);
myMockFn(); // 'original'
可以與非同步回呼一起使用。該方法必須被 await
,才能在之後使用原始實作。
test('async callback', async () => {
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
function mockRejectedValue(value: unknown): MockInstance<T>;
接受一個錯誤,該錯誤會在呼叫非同步函式時被拒絕。
const asyncMock = vi.fn().mockRejectedValue(new Error('Async error'));
await asyncMock(); // throws Error<'Async error'>
mockRejectedValueOnce
function mockRejectedValueOnce(value: unknown): MockInstance<T>;
接受一個值,該值會在下次函式呼叫期間被拒絕。如果串聯,每個連續的呼叫都會拒絕指定的值。
const asyncMock = vi
.fn()
.mockResolvedValueOnce('first call')
.mockRejectedValueOnce(new Error('Async error'));
await asyncMock(); // 'first call'
await asyncMock(); // throws Error<'Async error'>
mockReset
function mockReset(): MockInstance<T>;
執行 mockClear
的功能,並將內部實作重置為原始函式。 這也會重置所有「一次性」實作。
請注意,重置來自 vi.fn()
的模擬會將實作設定為一個傳回 undefined
的空函式。 重置來自 vi.fn(impl)
的模擬會將實作恢復為 impl
。
這對於將模擬重置為其原始狀態很有用。
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']]);
若要在每次測試之前自動呼叫此方法,請在設定中啟用 mockReset
。
mockRestore
function mockRestore(): MockInstance<T>;
執行 mockReset
的功能,並恢復被監視物件的原始描述器。
請注意,恢復來自 vi.fn()
的模擬會將實作設定為一個傳回 undefined
的空函式。 恢復來自 vi.fn(impl)
的模擬會將實作恢復為 impl
。
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([]);
若要在每次測試之前自動呼叫此方法,請在設定中啟用 restoreMocks
。
mockResolvedValue
function mockResolvedValue(value: Awaited<ReturnType<T>>): MockInstance<T>;
接受一個值,該值會在呼叫非同步函式時被解析。TypeScript 只會接受與原始函式傳回類型相符的值。
const asyncMock = vi.fn().mockResolvedValue(42);
await asyncMock(); // 42
mockResolvedValueOnce
function mockResolvedValueOnce(value: Awaited<ReturnType<T>>): MockInstance<T>;
接受一個值,該值會在下次函式呼叫期間被解析。TypeScript 只會接受與原始函式傳回類型相符的值。如果串聯,每個連續的呼叫都會解析指定的值。
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
function mockReturnThis(): MockInstance<T>;
如果您需要從方法傳回 this
上下文,而無需呼叫實際實作,請使用此方法。這是以下程式碼的簡寫:
spy.mockImplementation(function () {
return this;
});
mockReturnValue
function mockReturnValue(value: ReturnType<T>): MockInstance<T>;
接受一個值,該值會在每次呼叫模擬函式時傳回。TypeScript 只會接受與原始函式傳回類型相符的值。
const mock = vi.fn();
mock.mockReturnValue(42);
mock(); // 42
mock.mockReturnValue(43);
mock(); // 43
mockReturnValueOnce
function mockReturnValueOnce(value: ReturnType<T>): MockInstance<T>;
接受一個值,該值會在每次呼叫模擬函式時傳回。TypeScript 只會接受與原始函式傳回類型相符的值。
當模擬函式用完實作時,如果呼叫了 vi.fn(() => defaultValue)
或 .mockImplementation(() => defaultValue)
,它會呼叫預設實作:
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 calls: Parameters<T>[];
這是一個陣列,其中包含每次呼叫的所有參數。陣列中的一個項目即為該次呼叫的參數。
const fn = vi.fn();
fn('arg1', 'arg2');
fn('arg3');
fn.mock.calls ===
[
['arg1', 'arg2'], // first call
['arg3'], // second call
];
mock.lastCall
const lastCall: Parameters<T> | undefined;
此屬性包含上次呼叫的參數。如果模擬未被呼叫,它會傳回 undefined
。
mock.results
interface MockResultReturn<T> {
type: 'return';
/**
* The value that was returned from the function.
* If 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>>[];
這是一個陣列,其中包含從函式 傳回
的所有值。陣列中的一個項目是一個具有 type
和 value
屬性的物件。可用類型為:
'return'
- 函式傳回而未拋出錯誤。'throw'
- 函式拋出一個值。
value
屬性包含傳回值或拋出的錯誤。如果函式傳回 Promise
,則 result
會始終為 'return'
,即使 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.settledResults
interface MockSettledResultFulfilled<T> {
type: 'fulfilled';
value: T;
}
interface MockSettledResultRejected {
type: 'rejected';
value: any;
}
export type MockSettledResult<T> =
| MockSettledResultFulfilled<T>
| MockSettledResultRejected;
const settledResults: MockSettledResult<Awaited<ReturnType<T>>>[];
一個陣列,其中包含從函式 解決
或 拒絕
的所有值。
如果函式從未解決或拒絕,則此陣列會為空。
const fn = vi.fn().mockResolvedValueOnce('result');
const result = fn();
fn.mock.settledResults === [];
await result;
fn.mock.settledResults ===
[
{
type: 'fulfilled',
value: 'result',
},
];
mock.invocationCallOrder
const invocationCallOrder: number[];
此屬性傳回模擬函式的執行順序。它是一個數字陣列,在所有已定義的模擬之間共享。
const fn1 = vi.fn();
const fn2 = vi.fn();
fn1();
fn2();
fn1();
fn1.mock.invocationCallOrder === [1, 3];
fn2.mock.invocationCallOrder === [2];
mock.contexts
const contexts: ThisParameterType<T>[];
此屬性是一個陣列,其中包含在每次呼叫模擬函式期間使用的 this
值。
const fn = vi.fn();
const context = {};
fn.apply(context);
fn.call(context);
fn.mock.contexts[0] === context;
fn.mock.contexts[1] === context;
mock.instances
const instances: ReturnType<T>[];
此屬性是一個陣列,其中包含當模擬使用 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;