模擬函數 (Mock Functions)
你可以建立一個間諜函數 (spy function,或稱模擬函數) 來追蹤其執行情況,使用 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 參考描述了可用於操作間諜行為的屬性和方法。
getMockName
類型:
() => string
使用此方法來返回透過
.mockName(name)
方法設定的模擬函數名稱。
mockClear
類型:
() => MockInstance
清除關於每次呼叫的所有資訊。 呼叫它之後,
spy.mock.calls
和spy.mock.results
將返回空陣列。 如果你需要在不同的斷言之間清除間諜紀錄,這會很有用。如果你希望在每次測試之前自動呼叫此方法,你可以在配置中啟用
clearMocks
設定。
mockName
類型:
(name: string) => MockInstance
設定內部 mock 名稱。 有助於查看哪個 mock 未能通過斷言。
mockImplementation
類型:
(fn: Function) => MockInstance
接受一個函數,該函數將作為 mock 的實作。
例如:
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
接受一個函數,該函數將作為 mock 函數的單次呼叫的實作。 可以鏈式呼叫,以便多次函數呼叫產生不同的結果。
tsconst myMockFn = vi .fn() .mockImplementationOnce(() => true) .mockImplementationOnce(() => false); myMockFn(); // true myMockFn(); // false
當 mock 函數用完所有
mockImplementationOnce
設定的實作時,它將調用使用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>
在執行回調函式時,暫時覆蓋原始 mock 實作。
jsconst myMockFn = vi.fn(() => 'original'); myMockFn.withImplementation( () => 'temp', () => { myMockFn(); // 'temp' } ); myMockFn(); // 'original'
可以與非同步回調函式一起使用。 必須等待該方法完成,才能在之後使用原始實作。
tstest('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
接受一個將被拒絕的錯誤值,當呼叫非同步函數時。
tsconst asyncMock = vi.fn().mockRejectedValue(new Error('Async error')); await asyncMock(); // throws "Async error"
mockRejectedValueOnce
類型:
(value: any) => MockInstance
接受一個將被拒絕的值,用於 mock 函數的單次呼叫。 如果鏈式呼叫,則每個連續呼叫都將拒絕傳遞的值。
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
)。 當你想要將 mock 完全重置回其初始狀態時,這會很有用。如果你希望在每次測試之前自動呼叫此方法,你可以在配置中啟用
mockReset
設定。
mockRestore
類型:
() => MockInstance
執行
mockReset
的操作,並將內部實作恢復為原始函數。請注意,從
vi.fn()
還原模擬函數會將實作設定為返回undefined
的空函數。 恢復vi.fn(impl)
會將實作恢復為impl
。如果你希望在每次測試之前自動呼叫此方法,你可以在配置中啟用
restoreMocks
設定。
mockResolvedValue
類型:
(value: any) => MockInstance
接受一個將被解析的值,當呼叫非同步函數時。
tsconst asyncMock = vi.fn().mockResolvedValue(43); await asyncMock(); // 43
mockResolvedValueOnce
類型:
(value: any) => MockInstance
接受一個將被解析的值,用於 mock 函數的單次呼叫。 如果鏈式呼叫,則每個連續呼叫都將解析傳遞的值。
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
接受一個值,該值將在每次呼叫 mock 函數時返回。
tsconst mock = vi.fn(); mock.mockReturnValue(42); mock(); // 42 mock.mockReturnValue(43); mock(); // 43
mockReturnValueOnce
類型:
(value: any) => MockInstance
接受一個值,該值將被返回用於 mock 函數的單次呼叫。 如果鏈式呼叫,則每個連續呼叫都將返回傳遞的值。 當沒有更多
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'], // 第一次呼叫
['arg3', 'arg4'], // 第二次呼叫
];
mock.lastCall
這包含上次呼叫的參數。 如果沒有呼叫間諜函數,將返回 undefined
。
mock.results
這是一個陣列,包含從函數返回的所有值。 陣列的每個項目都是一個具有 type
和 value
屬性的物件。 可用的類型有:
'return'
- 函數返回而沒有拋出錯誤。'throw'
- 函數拋出了一個值。
value
屬性包含返回的值或拋出的錯誤。 如果函數返回一個 promise,當它解析時,value
屬性將變為該 promise 解析的值。
const fn = vi.fn();
const result = fn(); // 返回 'result'
try {
fn(); // 拋出錯誤
} catch {}
fn.mock.results ===
[
// 第一個結果
{
type: 'return',
value: '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;