測試執行器
WARNING
這是進階的 API。如果您只是想執行測試,您可能不需要這個。它主要供函式庫的作者使用。
您可以在設定檔案中使用 runner
選項指定測試執行器的路徑。此檔案應具有一個預設匯出,並包含一個實作以下方法的類別建構子:
export interface VitestRunner {
/**
* 在實際收集和執行測試之前,首先會被呼叫的方法。
*/
onBeforeCollect?: (paths: string[]) => unknown;
/**
* 在收集測試之後,並在 'onBeforeRun' 之前呼叫。
*/
onCollected?: (files: File[]) => unknown;
/**
* 當測試執行器應取消後續測試執行時呼叫。
* 執行器應監聽此方法,並在呼叫時將測試和套件在 "onBeforeRunSuite" 和 "onBeforeRunTask" 中標記為略過。
*/
onCancel?: (reason: CancelReason) => unknown;
/**
* 在執行單個測試之前呼叫。此時尚未有 'result'。
*/
onBeforeRunTest?: (test: TaskPopulated) => unknown;
/**
* 在實際執行測試函式之前呼叫。此時已經有帶有 "state" 和 "startTime" 的 "result"。
*/
onBeforeTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* 在設定結果和狀態後呼叫。
*/
onAfterRunTask?: (test: TaskPopulated) => unknown;
/**
* 在執行測試函式後立即呼叫。此時尚未有新狀態。如果測試函式拋出錯誤,則不會呼叫。
*/
onAfterTryTask?: (
test: TaskPopulated,
options: { retry: number; repeats: number }
) => unknown;
/**
* 在執行單個套件之前呼叫。此時尚未有 'result'。
*/
onBeforeRunSuite?: (suite: Suite) => unknown;
/**
* 在執行單個套件之後呼叫。此時具有狀態和結果。
*/
onAfterRunSuite?: (suite: Suite) => unknown;
/**
* 如果已定義,將取代 Vitest 通常的套件分割與處理。
* "before" 和 "after" 掛鉤將不會被忽略。
*/
runSuite?: (suite: Suite) => Promise<void>;
/**
* 如果已定義,將取代 Vitest 通常的處理。如果您有自訂測試函式,這會很有用。
* "before" 和 "after" 掛鉤將不會被忽略。
*/
runTask?: (test: TaskPopulated) => Promise<void>;
/**
* 當任務更新時呼叫。與報告器中的 "onTaskUpdate" 相同,但這在與測試相同的執行緒中執行。
*/
onTaskUpdate?: (task: [string, TaskResult | undefined][]) => Promise<void>;
/**
* 在執行收集路徑中的所有測試之前呼叫。
*/
onBeforeRunFiles?: (files: File[]) => unknown;
/**
* 在執行收集路徑中的所有測試後立即呼叫。
*/
onAfterRunFiles?: (files: File[]) => unknown;
/**
* 當定義新的測試上下文時呼叫。如果您想向上下文添加自訂屬性,這會很有用。
* 如果您只想使用執行器定義自訂上下文,請考慮改用 "setupFiles" 中的 "beforeAll"。
*
* 此方法會針對 "test" 和 "custom" 處理器呼叫。
*
* @see https://www.getbook.com/en/book/vitest-2/advanced/runner#your-task-function
*/
extendTaskContext?: <T extends Test | Custom>(
context: TaskContext<T>
) => TaskContext<T>;
/**
* 當匯入某些檔案時呼叫。可以在兩種情況下呼叫:收集測試時和匯入設定檔時。
*/
importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
/**
* 公開可用的設定。
*/
config: VitestRunnerConfig;
}
初始化此類別時,Vitest 會傳遞 Vitest 設定 - 您應該將其暴露為 config
屬性。
WARNING
Vitest 還會注入一個 ViteNodeRunner
實例作為 __vitest_executor
屬性。您可以使用它在 importFile
方法中處理檔案(這是 TestRunner
和 BenchmarkRunner
的預設行為)。
ViteNodeRunner
暴露了 executeId
方法,用於在 Vite 友善的環境中匯入測試檔案。這意味著它會在執行時解析匯入並轉換檔案內容,以便 Node 可以理解。
TIP
快照支援及其他一些功能取決於執行器。如果您不想失去它們,可以從 vitest/runners
匯入的 VitestTestRunner
擴充您的執行器。如果您想擴充基準測試功能,它也暴露了 BenchmarkNodeRunner
。
任務
套件和測試在內部稱為 tasks
(任務)。Vitest 執行器在收集任何測試之前會初始化一個 File
任務 - 這是 Suite
的超集合,並帶有一些額外的屬性。它在每個任務(包括 File
)上都作為 file
屬性可用。
interface File extends Suite {
/**
* 檔案所屬的池名稱。
* @default 'forks'
*/
pool?: string;
/**
* 檔案在 UNIX 格式下的路徑。
*/
filepath: string;
/**
* 檔案所屬的工作區專案名稱。
*/
projectName: string | undefined;
/**
* 收集檔案中所有測試所花費的時間。
* 此時間也包括匯入所有檔案依賴項的時間。
*/
collectDuration?: number;
/**
* 匯入設定檔所花費的時間。
*/
setupDuration?: number;
/**
* 檔案是否在未執行任何測試的情況下初始化。
* 這是為了讓 Vitest 在伺服器端填充狀態。
*/
local?: boolean;
}
每個套件都有一個 tasks
屬性,該屬性在收集階段填充。從上到下遍歷任務樹是很有用的。
interface Suite extends TaskBase {
type: 'suite';
/**
* 檔案任務。它是檔案的根任務。
*/
file: File;
/**
* 作為套件一部分的任務陣列。
*/
tasks: Task[];
}
每個任務都有一個 suite
屬性,該屬性引用其所在的套件。如果在頂層初始化 test
或 describe
,它們將不會有 suite
屬性(它不會等於 file
!)。File
也永遠沒有 suite
屬性。從下到上遍歷任務很有用。
interface Test<ExtraContext = object> extends TaskBase {
type: 'test';
/**
* 將會傳遞給測試函式的測試上下文。
*/
context: TaskContext<Test> & ExtraContext & TestContext;
/**
* 檔案任務。它是檔案的根任務。
*/
file: File;
/**
* 任務是否透過呼叫 `t.skip()` 而被跳過。
*/
pending?: boolean;
/**
* 如果任務失敗,是否應成功。如果任務失敗,它將被標記為通過。
*/
fails?: boolean;
/**
* 如果任務失敗將執行的掛鉤。順序取決於 `sequence.hooks` 選項。
*/
onFailed?: OnTestFailedHandler[];
/**
* 任務完成後將執行的掛鉤。順序取決於 `sequence.hooks` 選項。
*/
onFinished?: OnTestFinishedHandler[];
/**
* 儲存 Promise (來自非同步期望),以便在完成測試之前等待它們
*/
promises?: Promise<any>[];
}
每個任務都可以有一個 result
欄位。套件只有在套件回調或 beforeAll
/afterAll
回調中拋出錯誤阻止它們收集測試時才能擁有此欄位。測試在呼叫其回調後總是擁有此欄位 - state
和 errors
欄位根據結果而存在。如果在 beforeEach
或 afterEach
回調中拋出錯誤,則拋出的錯誤將存在於 task.result.errors
中。
export interface TaskResult {
/**
* 任務的狀態,並在收集期間繼承 `task.mode`。
* 當任務完成時,它將變更為 `pass` 或 `fail`。
* - **pass**: 任務成功執行
* - **fail**: 任務失敗
*/
state: TaskState;
/**
* 任務執行期間發生的錯誤;如果 `expect.soft()` 多次失敗,則可能會有多個錯誤。
*/
errors?: ErrorWithDiff[];
/**
* 任務執行所花費的時間(毫秒)。
*/
duration?: number;
/**
* 任務開始執行的時間(以毫秒計)。
*/
startTime?: number;
/**
* 任務完成後的堆大小(位元組)。
* 僅在設定 `logHeapUsage` 選項且定義了 `process.memoryUsage` 時可用。
*/
heap?: number;
/**
* 與此任務相關的掛鉤狀態,這在報告期間非常有用。
*/
hooks?: Partial<
Record<'afterAll' | 'beforeAll' | 'beforeEach' | 'afterEach', TaskState>
>;
/**
* 任務重試的次數;任務僅在失敗且設定了 `retry` 選項時才會重試。
*/
retryCount?: number;
/**
* 任務重複的次數。任務僅在設定了 `repeats` 選項時才會重複。此數字也包含 `retryCount`。
*/
repeatCount?: number;
}
您的任務函式
Vitest 公開了一個 Custom
任務類型,允許使用者重用內建報告器。它與 Test
幾乎相同,但類型為 'custom'
。
任務是套件的一部分物件。它會自動使用 suite.task
方法添加到當前套件:
// ./utils/custom.js
import { createTaskCollector, getCurrentSuite, setFn } from 'vitest/suite';
export { afterAll, beforeAll, describe } from 'vitest';
// 此函式將在收集階段呼叫:
// 不要在此處呼叫函式處理器,而是將其添加到套件任務中
// 使用 "getCurrentSuite().task()" 方法
// 注意:createTaskCollector 提供對 "todo"/"each"/... 的支援
export const myCustomTask = createTaskCollector(function (name, fn, timeout) {
getCurrentSuite().task(name, {
...this, // 以便正確追蹤 "todo"/"skip"/...
meta: {
customPropertyToDifferentiateTask: true,
},
handler: fn,
timeout,
});
});
// ./garden/tasks.test.js
import { afterAll, beforeAll, describe, myCustomTask } from '../custom.js';
import { gardener } from './gardener.js';
describe('take care of the garden', () => {
beforeAll(() => {
gardener.putWorkingClothes();
});
myCustomTask('weed the grass', () => {
gardener.weedTheGrass();
});
myCustomTask.todo('mow the lawn', () => {
gardener.mowerTheLawn();
});
myCustomTask('water flowers', () => {
gardener.waterFlowers();
});
afterAll(() => {
gardener.goHome();
});
});
vitest ./garden/tasks.test.js
WARNING
如果您沒有自訂執行器或未定義 runTest
方法,Vitest 將會嘗試自動獲取任務。如果您沒有使用 setFn
添加函式,它將失敗。