Vi
Vitest 透過其 vi
輔助函式提供實用函式,以協助您進行測試。您可以全域存取它(當啟用 全域配置 時),或直接從 vitest
匯入它:
import { vi } from 'vitest';
模擬模組功能
本節介紹當您模擬模組時可以使用的 API。請注意,Vitest 不支援模擬透過 require()
匯入的模組。
vi.mock
- 類型:
(path: string, factory?: (importOriginal: () => unknown) => unknown) => void
將指定 path
的所有匯入模組替換為另一個模組。您可以在路徑中使用配置的 Vite 別名。對 vi.mock
的呼叫會被提升,因此呼叫它的位置不影響結果。它將始終在所有匯入之前執行。如果您需要引用範圍外的變數,可以在 vi.hoisted
中定義它們,然後在 vi.mock
中引用。
WARNING
vi.mock
僅適用於使用 import
關鍵字匯入的模組,不適用於 require
。
為了提升 vi.mock
,Vitest 會靜態分析您的檔案。這表示無法使用未直接從 vitest
套件匯入的 vi
(例如,從某些工具檔案中匯入)。請使用從 vitest
匯入的 vi
來呼叫 vi.mock
,或啟用 globals
配置選項。
Vitest 不會模擬在 設定檔案 中匯入的模組,因為在測試檔案執行時,這些模組已經被快取。您可以在 vi.hoisted
內呼叫 vi.resetModules()
,以在執行測試檔案之前清除所有模組快取。
如果定義了 factory
,則所有匯入都將返回其結果。Vitest 僅呼叫 factory
一次,並快取所有後續匯入的結果,直到呼叫 vi.unmock
或 vi.doUnmock
。
與 jest
不同,factory
可以是非同步的。您可以使用 vi.importActual
或一個輔助函式,其中 factory
作為第一個引數傳入,並在內部取得原始模組。
import { vi } from 'vitest';
// ---cut---
// 當使用 JavaScript 時
vi.mock('./path/to/module.js', async importOriginal => {
const mod = await importOriginal();
return {
...mod,
// 替換一些匯出
namedExport: vi.fn(),
};
});
// 當使用 TypeScript 時
vi.mock('./path/to/module.js', async importOriginal => {
const mod = await importOriginal<typeof import('./path/to/module.js')>();
return {
...mod,
// 替換一些匯出
namedExport: vi.fn(),
};
});
WARNING
vi.mock
會被提升(換句話說,移動)到檔案頂部。這表示無論您在何處編寫它(無論是在 beforeEach
還是 test
內部),它實際上都會在那之前被呼叫。
這也表示您無法在 factory
內部使用在 factory
外部定義的任何變數。
如果您需要在 factory
內部使用變數,請嘗試 vi.doMock
。它的工作方式相同,但不會被提升。請注意,它只會模擬後續的匯入。
如果 vi.hoisted
方法在 vi.mock
之前宣告,您也可以參考它定義的變數:
import { namedExport } from './path/to/module.js';
const mocks = vi.hoisted(() => {
return {
namedExport: vi.fn(),
};
});
vi.mock('./path/to/module.js', () => {
return {
namedExport: mocks.namedExport,
};
});
vi.mocked(namedExport).mockReturnValue(100);
expect(namedExport()).toBe(100);
expect(namedExport).toBe(mocks.namedExport);
WARNING
如果您要模擬具有預設匯出的模組,則需要在返回的 factory
函式物件中提供一個 default
鍵。這是一個 ES 模組特定的注意事項;因此,jest
文件可能會有所不同,因為 jest
使用 CommonJS 模組。例如,
vi.mock('./path/to/module.js', () => {
return {
default: { myDefaultKey: vi.fn() },
namedExport: vi.fn(),
// 等
};
});
如果有一個與您要模擬的檔案並排的 __mocks__
資料夾,並且未提供 factory
,Vitest 將嘗試在 __mocks__
子資料夾中找到具有相同名稱的檔案,並將其用作實際模組。如果您要模擬一個依賴項,Vitest 將嘗試在專案的 根目錄 中找到一個 __mocks__
資料夾(預設為 process.cwd()
)。您可以透過 deps.moduleDirectories 配置選項告訴 Vitest 依賴項的位置。
例如,您有以下檔案結構:
- __mocks__
- axios.js
- src
__mocks__
- increment.js
- increment.js
- tests
- increment.test.js
如果您在測試檔案中呼叫 vi.mock
而未提供 factory
,則它會在 __mocks__
資料夾中尋找一個檔案作為模組:
// increment.test.js
import { vi } from 'vitest';
// axios 是來自 `__mocks__/axios.js` 的預設匯出
import axios from 'axios';
// increment 是來自 `src/__mocks__/increment.js` 的具名匯出
import { increment } from '../increment.js';
vi.mock('axios');
vi.mock('../increment.js');
axios.get(`/apples/${increment(1)}`);
WARNING
請注意,如果您不呼叫 vi.mock
,則模組不會自動模擬。若要複製 Jest 的自動模擬行為,您可以為 setupFiles
內的每個所需模組呼叫 vi.mock
。
如果沒有 __mocks__
資料夾或提供 factory
,Vitest 將匯入原始模組並自動模擬其所有匯出。有關套用的規則,請參閱 演算法。
vi.doMock
- 類型:
(path: string, factory?: (importOriginal: () => unknown) => unknown) => void
與 vi.mock
相同,但它不會被提升到檔案頂部,因此您可以在全域檔案範圍內引用變數。模組的下一個 動態匯入 將被模擬。
WARNING
這不會模擬在此之前已匯入的模組。請注意,ESM 中的所有靜態匯入都會被提升,因此將其放在靜態匯入之前不會強制它在匯入之前被呼叫:
vi.doMock('./increment.js'); // 這會在匯入語句 _之後_ 執行
import { increment } from './increment.js';
// ./increment.js
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// 模組未被模擬,因為 vi.doMock 尚未被呼叫
increment(1) === 2;
let mockedIncrement = 100;
beforeEach(() => {
// 您可以在 factory 內部存取變數
vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }));
});
test('importing the next module imports mocked one', async () => {
// 原始匯入未被模擬,因為 vi.doMock 在匯入之後被評估
expect(increment(1)).toBe(2);
const { increment: mockedIncrement } = await import('./increment.js');
// 新的動態匯入傳回模擬模組
expect(mockedIncrement(1)).toBe(101);
expect(mockedIncrement(1)).toBe(102);
expect(mockedIncrement(1)).toBe(103);
});
vi.mocked
- 類型:
<T>(obj: T, deep?: boolean) => MaybeMockedDeep<T>
- 類型:
<T>(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep<T>
TypeScript 的型別輔助工具。單純返回傳入的物件。
當 partial
為 true
時,它將期望 Partial<T>
作為返回的值。預設情況下,這只會讓 TypeScript 相信第一層值已被模擬。您可以傳遞 { deep: true }
作為第二個引數,以告知 TypeScript 整個物件已被模擬(如果確實如此)。
import example from './example.js';
vi.mock('./example.js');
test('1 + 1 equals 10', async () => {
vi.mocked(example.calc).mockReturnValue(10);
expect(example.calc(1, '+', 1)).toBe(10);
});
vi.importActual
- 類型:
<T>(path: string) => Promise<T>
匯入模組,並略過所有關於是否應模擬的檢查。如果您想部分模擬模組,這可能很有用。
vi.mock('./example.js', async () => {
const axios = await vi.importActual('./example.js');
return { ...axios, get: vi.fn() };
});
vi.importMock
- 類型:
<T>(path: string) => Promise<MaybeMockedDeep<T>>
匯入一個模組,其中所有屬性(包括巢狀屬性)都被模擬。遵循與 vi.mock
相同的規則。有關套用的規則,請參閱 演算法。
vi.unmock
- 類型:
(path: string) => void
從模擬註冊表中移除模組。所有對匯入的呼叫都將返回原始模組,即使它之前已被模擬。此呼叫會被提升到檔案頂部,因此它只會取消模擬在 setupFiles
中定義的模組,例如。
vi.doUnmock
- 類型:
(path: string) => void
與 vi.unmock
相同,但不會被提升到檔案頂部。模組的下一個匯入將匯入原始模組而不是模擬模組。這不會取消模擬先前匯入的模組。
// ./increment.js
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment 已經被模擬,因為 vi.mock 被提升
increment(1) === 100;
// 這被提升,並且 factory 在第 1 行的匯入之前被呼叫
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// 所有呼叫都被模擬,並且 `increment` 始終返回 100
increment(1) === 100;
increment(30) === 100;
// 這沒有被提升,因此其他匯入將返回未模擬的模組
vi.doUnmock('./increment.js');
// 這仍然返回 100,因為 `vi.doUnmock` 不會重新評估模組
increment(1) === 100;
increment(30) === 100;
// 下一個匯入未被模擬,現在 `increment` 是返回 count + 1 的原始函式
const { increment: unmockedIncrement } = await import('./increment.js');
unmockedIncrement(1) === 2;
unmockedIncrement(30) === 31;
vi.resetModules
- 類型:
() => Vitest
透過清除所有模組的快取來重設模組註冊表。這允許在重新匯入時重新評估模組。無法重新評估頂層匯入。在測試中,若模組的本地狀態發生衝突,此方法可能對於隔離這些模組很有用。
import { vi } from 'vitest';
import { data } from './data.js'; // 不會在每個測試之前重新評估
beforeEach(() => {
vi.resetModules();
});
test('change state', async () => {
const mod = await import('./some/path.js'); // 將被重新評估
mod.changeLocalState('new value');
expect(mod.getLocalState()).toBe('new value');
});
test('module has old state', async () => {
const mod = await import('./some/path.js'); // 將被重新評估
expect(mod.getLocalState()).toBe('old value');
});
WARNING
不會重設模擬註冊表。若要清除模擬註冊表,請使用 vi.unmock
或 vi.doUnmock
。
vi.dynamicImportSettled
等待所有匯入載入。如果您有一個同步呼叫,且此呼叫會開始匯入一個您無法以其他方式等待的模組,這會很有用。
import { expect, test } from 'vitest';
// 無法追蹤匯入,因為未傳回 Promise
function renderComponent() {
import('./component.js').then(({ render }) => {
render();
});
}
test('operations are resolved', async () => {
renderComponent();
await vi.dynamicImportSettled();
expect(document.querySelector('.component')).not.toBeNull();
});
TIP
如果在動態匯入期間啟動了另一個動態匯入,則此方法將等待所有這些匯入都已解析。
此方法還將等待匯入解析後的下一個 setTimeout
刻度,因此所有同步操作都應在解析時完成。
模擬函式和物件
本節介紹如何使用 方法模擬 並替換環境和全域變數。
vi.fn
- 類型:
(fn?: Function) => Mock
建立函式的監視函數,即使沒有函式也能啟動。每次呼叫函式時,它都會儲存其呼叫引數、返回的值和實例。此外,您可以使用 方法 來操作其行為。 如果未給定函式,則模擬將在呼叫時返回 undefined
。
import { expect, vi } from 'vitest';
// ---cut---
const getApples = vi.fn(() => 0);
getApples();
expect(getApples).toHaveBeenCalled();
expect(getApples).toHaveReturnedWith(0);
getApples.mockReturnValueOnce(5);
const res = getApples();
expect(res).toBe(5);
expect(getApples).toHaveNthReturnedWith(2, 5);
vi.isMockFunction
- 類型:
(fn: Function) => boolean
檢查給定的參數是否為模擬函式。如果您使用 TypeScript,它也會縮限其型別範圍。
vi.clearAllMocks
將在所有監視函數上呼叫 .mockClear()
。這會清除模擬的呼叫記錄,但不會將其實現重置為預設值。
vi.resetAllMocks
將在所有監視函數上呼叫 .mockReset()
。這會清除模擬的呼叫記錄,並將實現重置為空函式 (會返回 undefined
)。
vi.restoreAllMocks
將在所有監視函數上呼叫 .mockRestore()
。這會清除模擬的呼叫記錄,並將實現重置為原始值。
vi.spyOn
- 類型:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
建立物件的方法或 getter/setter 的監視函數,類似於 vi.fn()
。它返回一個 模擬函式。
import { expect, vi } from 'vitest';
// ---cut---
let apples = 0;
const cart = {
getApples: () => 42,
};
const spy = vi.spyOn(cart, 'getApples').mockImplementation(() => apples);
apples = 1;
expect(cart.getApples()).toBe(1);
expect(spy).toHaveBeenCalled();
expect(spy).toHaveReturnedWith(1);
TIP
您可以在 afterEach
內部呼叫 vi.restoreAllMocks
(或啟用 test.restoreMocks
)以將所有方法還原為其原始實現。這將還原原始 物件描述符,因此您將無法變更方法的實現:
const cart = {
getApples: () => 42,
};
const spy = vi.spyOn(cart, 'getApples').mockReturnValue(10);
console.log(cart.getApples()); // 10
vi.restoreAllMocks();
console.log(cart.getApples()); // 42
spy.mockReturnValue(10);
console.log(cart.getApples()); // 仍然是 42!
vi.stubEnv 0.26.0+
- 類型:
(name: string, value: string) => Vitest
變更 process.env
和 import.meta.env
中環境變數的值。您可以透過呼叫 vi.unstubAllEnvs
來還原其值。
import { vi } from 'vitest';
// `process.env.NODE_ENV` 和 `import.meta.env.NODE_ENV`
// 在呼叫 "vi.stubEnv" 之前是 "development"
vi.stubEnv('NODE_ENV', 'production');
process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';
// 不會變更其他環境變數
import.meta.env.MODE === 'development';
TIP
您也可以透過簡單地指派值來變更值,但您將無法使用 vi.unstubAllEnvs
來還原先前的值:
import.meta.env.MODE = 'test';
vi.unstubAllEnvs 0.26.0+
- 類型:
() => Vitest
還原所有透過 vi.stubEnv
變更的 import.meta.env
和 process.env
值。第一次呼叫時,Vitest 會記住原始值並儲存它,直到再次呼叫 unstubAllEnvs
。
import { vi } from 'vitest';
// `process.env.NODE_ENV` 和 `import.meta.env.NODE_ENV`
// 在呼叫 stubEnv 之前是 "development"
vi.stubEnv('NODE_ENV', 'production');
process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';
vi.stubEnv('NODE_ENV', 'staging');
process.env.NODE_ENV === 'staging';
import.meta.env.NODE_ENV === 'staging';
vi.unstubAllEnvs();
// 還原為在第一次呼叫 "stubEnv" 之前儲存的值
process.env.NODE_ENV === 'development';
import.meta.env.NODE_ENV === 'development';
vi.stubGlobal
- 類型:
(name: string | number | symbol, value: unknown) => Vitest
變更全域變數的值。您可以透過呼叫 vi.unstubAllGlobals
來還原其原始值。
import { vi } from 'vitest';
// 在呼叫 stubGlobal 之前,`innerWidth` 為 "0"
vi.stubGlobal('innerWidth', 100);
innerWidth === 100;
globalThis.innerWidth === 100;
// 如果您使用 jsdom 或 happy-dom
window.innerWidth === 100;
TIP
您也可以透過簡單地將其指派值給 globalThis
或 window
(如果您使用 jsdom
或 happy-dom
環境)來變更值,但您將無法使用 vi.unstubAllGlobals
來還原原始值:
globalThis.innerWidth = 100;
// 如果您使用 jsdom 或 happy-dom
window.innerWidth = 100;
vi.unstubAllGlobals 0.26.0+
- 類型:
() => Vitest
還原所有透過 vi.stubGlobal
變更的 globalThis
/global
(以及 window
/top
/self
/parent
,如果您使用 jsdom
或 happy-dom
環境) 中的全域值。第一次呼叫時,Vitest 會記住原始值並儲存它,直到再次呼叫 unstubAllGlobals
。
import { vi } from 'vitest';
const Mock = vi.fn();
// 在呼叫 "stubGlobal" 之前,IntersectionObserver 為 "undefined"
vi.stubGlobal('IntersectionObserver', Mock);
IntersectionObserver === Mock;
global.IntersectionObserver === Mock;
globalThis.IntersectionObserver === Mock;
// 如果您使用 jsdom 或 happy-dom
window.IntersectionObserver === Mock;
vi.unstubAllGlobals();
globalThis.IntersectionObserver === undefined;
'IntersectionObserver' in globalThis === false;
// 會拋出 ReferenceError 錯誤,因為它未定義
IntersectionObserver === undefined;
模擬計時器
本節描述如何使用 模擬計時器。
vi.advanceTimersByTime
- 類型:
(ms: number) => Vitest
此方法會呼叫所有已啟動的計時器,直到經過指定的毫秒數或佇列為空,以先到者為準。
import { vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersByTime(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersByTimeAsync
- 類型:
(ms: number) => Promise<Vitest>
此方法會呼叫所有已啟動的計時器,直到經過指定的毫秒數或佇列為空,以先到者為準。 這包含非同步設定的計時器。
import { vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersByTimeAsync(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersToNextTimer
- 類型:
() => Vitest
呼叫下一個可用的計時器。 適用於在每次計時器呼叫之間進行斷言檢查。 您可以鏈式呼叫此方法來自行管理計時器。
import { vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- 類型:
() => Promise<Vitest>
呼叫下一個可用的計時器,並等待其解析(如果它是非同步設定的)。 適用於在每次計時器呼叫之間進行斷言檢查。
import { expect, vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersToNextTimerAsync(); // log: 1
expect(console.log).toHaveBeenCalledWith(1);
await vi.advanceTimersToNextTimerAsync(); // log: 2
await vi.advanceTimersToNextTimerAsync(); // log: 3
vi.getTimerCount
- 類型:
() => number
取得等待執行的計時器數量。
vi.clearAllTimers
移除所有已排程執行的計時器。 這些計時器將永遠不會執行。
vi.getMockedSystemTime
- 類型:
() => Date | null
回傳使用 setSystemTime
設定的模擬目前日期。 如果日期未被模擬,則此方法將回傳 null
。
vi.getRealSystemTime
- 類型:
() => number
當使用 vi.useFakeTimers
時,Date.now
呼叫會被模擬。 如果您需要以毫秒為單位取得真實時間,您可以呼叫此函式。
vi.runAllTicks
- 類型:
() => Vitest
呼叫由 process.nextTick
排隊的每個微任務。 這也會執行所有由它們自己排程的微任務。
vi.runAllTimers
- 類型:
() => Vitest
此方法會呼叫所有已啟動的計時器,直到計時器佇列為空。 這表示在 runAllTimers
期間呼叫的每個計時器都將被執行。 如果您有一個無限間隔,它將在 10,000 次嘗試後拋出錯誤(可以使用 fakeTimers.loopLimit
進行設定)。
import { vi } from 'vitest';
// ---cut---
let i = 0;
setTimeout(() => console.log(++i));
const interval = setInterval(() => {
console.log(++i);
if (i === 3) clearInterval(interval);
}, 50);
vi.runAllTimers();
// log: 1
// log: 2
// log: 3
vi.runAllTimersAsync
- 類型:
() => Promise<Vitest>
此方法會非同步呼叫所有已啟動的計時器,直到計時器佇列為空。 這表示在 runAllTimersAsync
期間呼叫的每個計時器都將被執行,即使是非同步計時器。 如果您有一個無限間隔,它將在 10,000 次嘗試後拋出錯誤(可以使用 fakeTimers.loopLimit
進行設定)。
import { vi } from 'vitest';
// ---cut---
setTimeout(async () => {
console.log(await Promise.resolve('result'));
}, 100);
await vi.runAllTimersAsync();
// log: result
vi.runOnlyPendingTimers
- 類型:
() => Vitest
此方法會呼叫在 vi.useFakeTimers
呼叫之後啟動的每個計時器。 它不會執行在其呼叫期間啟動的任何計時器。
import { vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => console.log(++i), 50);
vi.runOnlyPendingTimers();
// log: 1
vi.runOnlyPendingTimersAsync
- 類型:
() => Promise<Vitest>
此方法會非同步呼叫在 vi.useFakeTimers
呼叫之後啟動的每個計時器,即使是非同步計時器。 它不會執行在其呼叫期間啟動的任何計時器。
import { vi } from 'vitest';
// ---cut---
setTimeout(() => {
console.log(1);
}, 100);
setTimeout(() => {
Promise.resolve().then(() => {
console.log(2);
setInterval(() => {
console.log(3);
}, 40);
});
}, 10);
await vi.runOnlyPendingTimersAsync();
// log: 2
// log: 3
// log: 3
// log: 1
vi.setSystemTime
- 類型:
(date: string | number | Date) => void
如果啟用了偽造計時器,此方法會模擬使用者變更系統時鐘(將影響日期相關的 API,例如 hrtime
、performance.now
或 new Date()
),但它不會觸發任何計時器。 如果未啟用偽造計時器,此方法只會模擬 Date.*
呼叫。
如果您需要測試任何依賴目前日期的內容(例如程式碼中的 Luxon 呼叫),這會很有用。
import { expect, vi } from 'vitest';
// ---cut---
const date = new Date(1998, 11, 19);
vi.useFakeTimers();
vi.setSystemTime(date);
expect(Date.now()).toBe(date.valueOf());
vi.useRealTimers();
vi.useFakeTimers
- 類型:
(config?: FakeTimerInstallOpts) => Vitest
要啟用模擬計時器,您需要呼叫此方法。它將封裝所有後續對計時器的呼叫(例如 setTimeout
、setInterval
、clearTimeout
、clearInterval
、setImmediate
、clearImmediate
和 Date
),直到呼叫 vi.useRealTimers()
為止。
當使用 --pool=forks
在 node:child_process
內部執行 Vitest 時,不支援模擬 nextTick
。 NodeJS 在 node:child_process
內部使用 process.nextTick
,且在被模擬時會停止回應。 當使用 --pool=threads
執行 Vitest 時,支援模擬 nextTick
。
該實現內部基於 @sinonjs/fake-timers
。
TIP
自 0.35.0
版本起,vi.useFakeTimers()
不再自動模擬 process.nextTick
。 仍然可以透過在 toFake
參數中指定選項來模擬它:vi.useFakeTimers({ toFake: ['nextTick'] })
。
vi.isFakeTimers 0.34.5+
- 類型:
() => boolean
如果啟用了偽造計時器,則回傳 true
。
vi.useRealTimers
- 類型:
() => Vitest
當計時器測試結束時,您可以呼叫此方法以將模擬計時器恢復為其原始實作。 之前排程的所有計時器都將被丟棄。
其他功能
Vitest 提供的一組有用的輔助函式。
vi.waitFor 0.34.5+
- 類型:
<T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
等待回呼函式成功執行。 如果回呼函式拋出錯誤或傳回 rejected 的 Promise,它將繼續等待,直到成功或逾時。
當您需要等待某些非同步操作完成時,這非常有用,例如,當您啟動伺服器並需要等待它啟動時。
import { expect, test, vi } from 'vitest';
import { createServer } from './server.js';
test('Server started successfully', async () => {
const server = createServer();
await vi.waitFor(
() => {
if (!server.isReady) throw new Error('Server not started');
console.log('Server started');
},
{
timeout: 500, // default is 1000
interval: 20, // default is 50
}
);
expect(server.isReady).toBe(true);
});
它也適用於非同步回呼函式
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';
test('Element exists in a DOM', async () => {
// start populating DOM
populateDOMAsync();
const element = await vi.waitFor(
async () => {
// try to get the element until it exists
const element = (await getDOMElementAsync()) as HTMLElement | null;
expect(element).toBeTruthy();
expect(element.dataset.initialized).toBeTruthy();
return element;
},
{
timeout: 500, // default is 1000
interval: 20, // default is 50
}
);
expect(element).toBeInstanceOf(HTMLElement);
});
如果使用 vi.useFakeTimers
,vi.waitFor
會在每次檢查回呼函式中自動呼叫 vi.advanceTimersByTime(interval)
。
vi.waitUntil 0.34.5+
- 類型:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
這與 vi.waitFor
類似,但如果回呼函式拋出任何錯誤,執行會立即中斷並收到錯誤訊息。 如果回呼函式傳回假值,則下一個檢查將繼續,直到傳回真值。 當您需要等待某個東西存在才能執行下一步時,這很有用。
看看下面的例子。 我們可以使用 vi.waitUntil
來等待元素出現在頁面上,然後我們可以對該元素做一些事情。
import { expect, test, vi } from 'vitest';
test('Element render correctly', async () => {
const element = await vi.waitUntil(() => document.querySelector('.element'), {
timeout: 500, // default is 1000
interval: 20, // default is 50
});
// do something with the element
expect(element.querySelector('.element-child')).toBeTruthy();
});
vi.hoisted 0.31.0+
- 類型:
<T>(factory: () => T) => T
ES 模組中的所有靜態 import
語句都會被提升到檔案的頂部,因此在匯入之前定義的任何程式碼實際上都會在評估匯入之後執行。
但是,在匯入模組之前呼叫一些副作用(例如模擬日期)可能會很有用。
為了繞過這個限制,您可以將靜態導入改寫為動態導入,如下所示:
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
執行 vitest
時,您可以使用 vi.hoisted
方法自動執行此操作。
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
此方法傳回從工廠函式傳回的值。 如果您需要輕鬆存取本地定義的變數,您可以在 vi.mock
工廠函式中使用該值:
import { expect, vi } from 'vitest';
import { originalMethod } from './path/to/module.js';
const { mockedMethod } = vi.hoisted(() => {
return { mockedMethod: vi.fn() };
});
vi.mock('./path/to/module.js', () => {
return { originalMethod: mockedMethod };
});
mockedMethod.mockReturnValue(100);
expect(originalMethod()).toBe(100);
請注意,即使您的環境不支援頂層的 await,也可以非同步呼叫此方法:
const promised = await vi.hoisted(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
return response.json();
});
vi.setConfig
- 類型:
RuntimeConfig
更新目前測試檔案的設定。 此方法僅支援會影響目前測試檔案的設定選項:
vi.setConfig({
allowOnly: true,
testTimeout: 10_000,
hookTimeout: 10_000,
clearMocks: true,
restoreMocks: true,
fakeTimers: {
now: new Date(2021, 11, 19),
// supports the whole object
},
maxConcurrency: 10,
sequence: {
hooks: 'stack',
// supports only "sequence.hooks"
},
});
vi.resetConfig
- 類型:
RuntimeConfig
如果之前呼叫了 vi.setConfig
,這會將設定重置為原始狀態。