Skip to content
Vitest 3
Main Navigation 指南 & API配置瀏覽器模式進階 API
3.2.0
2.1.9
1.6.1
0.34.6

繁體中文

English
简体中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

繁體中文

English
简体中文
Español
Français
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

外觀

Sidebar Navigation

API

Node API

進階 API

Vitest API

TestProject

TestSpecification

Test Task API

TestCase

TestSuite

TestModule

TestCollection

插件 API

執行器 API

報告器 API

任務中繼資料

指南

執行測試

擴展報告器

自訂 Pool

配置參考

測試 API 參考

本頁導覽

執行器 API ​

WARNING

這是一個高階 API。如果您只是想執行測試,可能不需要使用此 API。它主要供程式庫作者使用。

您可以在設定檔中使用 runner 選項指定測試執行器的路徑。此檔案應預設匯出一個類別建構函式,該建構函式實作以下方法:

ts
export interface VitestRunner {
  /**
   * 在實際收集並執行測試之前,會最先呼叫此方法。
   */
  onBeforeCollect?: (paths: string[]) => unknown;
  /**
   * 在收集測試之後,以及 "onBeforeRun" 之前呼叫。
   */
  onCollected?: (files: File[]) => unknown;

  /**
   * 當測試執行器應取消下一次測試執行時呼叫。
   * 執行器應監聽此方法,並在被呼叫時,於
   * "onBeforeRunSuite" 和 "onBeforeRunTask" 中將測試和套件標記為跳過。
   */
  onCancel?: (reason: CancelReason) => unknown;

  /**
   * 在執行單個測試之前呼叫。此時尚未產生 "result" 值。
   */
  onBeforeRunTask?: (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, TaskMeta | undefined][]
  ) => Promise<void>;

  /**
   * 在執行收集路徑中的所有測試之前呼叫。
   */
  onBeforeRunFiles?: (files: File[]) => unknown;
  /**
   * 在執行收集路徑中的所有測試之後立即呼叫。
   */
  onAfterRunFiles?: (files: File[]) => unknown;
  /**
   * 當定義新的測試上下文時呼叫。如果您想向該上下文添加自訂屬性,這會很有用。
   * 如果您只想透過執行器定義自訂上下文,請考慮使用 "setupFiles" 中的 "beforeAll" 來定義自訂上下文。
   */
  extendTaskContext?: (context: TestContext) => TestContext;
  /**
   * 當匯入某些檔案時呼叫。此方法可在兩種情況下呼叫:收集測試,以及匯入設定檔。
   */
  importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
  /**
   * 當 `test.extend` 與 `{ injected: true }` 一起使用時,執行器嘗試獲取其值時會呼叫此函式。
   */
  injectValue?: (key: string) => unknown;
  /**
   * 公開可用的配置。
   */
  config: VitestRunnerConfig;
  /**
   * 當前池的名稱。可能會影響伺服器端呼叫堆疊的推斷。
   */
  pool?: string;
}

初始化此類別時,Vitest 會傳入其配置,您應將其作為 config 屬性公開:

ts
import type { RunnerTestFile } from 'vitest';
import type { VitestRunner, VitestRunnerConfig } from 'vitest/suite';
import { VitestTestRunner } from 'vitest/runners';

class CustomRunner extends VitestTestRunner implements VitestRunner {
  public config: VitestRunnerConfig;

  constructor(config: VitestRunnerConfig) {
    this.config = config;
  }

  onAfterRunFiles(files: RunnerTestFile[]) {
    console.log('finished running', files);
  }
}

export default CustomRunner;

WARNING

Vitest 也會將 ViteNodeRunner 的實例注入為 __vitest_executor 屬性。您可以使用它在 importFile 方法中處理檔案(這是 TestRunner 和 BenchmarkRunner 的預設行為)。

ViteNodeRunner 暴露了 executeId 方法,用於在 Vite 友善的環境中匯入測試檔案。這表示它會在執行時解析匯入並轉換檔案內容,以便 Node 能夠理解:

ts
export default class Runner {
  async importFile(filepath: string) {
    await this.__vitest_executor.executeId(filepath);
  }
}

WARNING

如果您沒有自訂執行器或未定義 runTask 方法,Vitest 將嘗試自動取得任務。如果您沒有使用 setFn 添加函式,則會失敗。

TIP

快照功能支援及其他一些功能取決於執行器。如果您不想失去這些功能,可以從 vitest/runners 匯入 VitestTestRunner 並擴展您的執行器。如果您想擴展效能基準測試功能,它也提供了 BenchmarkNodeRunner。

任務 ​

WARNING

「執行器任務 API」是實驗性的,主要應僅在測試執行期間使用。Vitest 也提供了「報告任務 API」(./api/test-module),在主執行緒中(例如在報告器內部)工作時應該優先考慮使用。

團隊目前正在討論未來是否應以「報告任務」取代「執行器任務」。

套件和測試在內部稱為 tasks。Vitest 執行器在收集任何測試之前會初始化一個 File 任務——這是 Suite 的超集合,並帶有一些額外屬性。它在每個任務(包括 File)上都可作為 file 屬性使用。

ts
interface File extends Suite {
  /**
   * 檔案所屬的池名稱。
   * @default 'forks'
   */
  pool?: string;
  /**
   * 檔案的 UNIX 格式路徑。
   */
  filepath: string;
  /**
   * 此檔案所屬的測試專案名稱。
   */
  projectName: string | undefined;
  /**
   * 收集檔案中所有測試所花費的時間。
   * 此時間也包含匯入所有檔案的依賴項。
   */
  collectDuration?: number;
  /**
   * 匯入設定檔所花費的時間。
   */
  setupDuration?: number;
}

每個套件都有一個 tasks 屬性,該屬性會在收集階段被填入。這有助於從上到下遍歷任務樹。

ts
interface Suite extends TaskBase {
  type: 'suite';
  /**
   * 檔案任務。它是此檔案的根任務。
   */
  file: File;
  /**
   * 屬於此套件的任務陣列。
   */
  tasks: Task[];
}

每個任務都有一個 suite 屬性,指向其所屬的套件。如果 test 或 describe 在頂層被初始化,它們將沒有 suite 屬性(它將不等於 file!)。File 也從不具有 suite 屬性。這有助於從下到上遍歷任務。

ts
interface Test<ExtraContext = object> extends TaskBase {
  type: 'test';
  /**
   * 將傳遞給測試函式的上下文。
   */
  context: TestContext & ExtraContext;
  /**
   * 檔案任務。它是此檔案的根任務。
   */
  file: File;
  /**
   * 此任務是否因呼叫 `context.skip()` 而被跳過。
   */
  pending?: boolean;
  /**
   * 此任務即使失敗是否仍應被視為成功。若任務失敗,它將被標記為通過。
   */
  fails?: boolean;
  /**
   * 儲存 Promise(來自非同步期望),以便在測試完成前等待它們。
   */
  promises?: Promise<any>[];
}

每個任務都可以包含 result 欄位。套件僅在套件回呼或 beforeAll/afterAll 回呼中拋出錯誤,導致無法收集測試時,才會包含此欄位。測試在呼叫其回呼後總是包含此欄位——state 和 errors 欄位會根據結果是否存在。若在 beforeEach 或 afterEach 回呼中拋出錯誤,則該錯誤將存在於 task.result.errors 中。

ts
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 提供了 createTaskCollector 工具,用於建立您自己的 test 方法。它的行為與測試相同,但在收集期間會呼叫一個自訂方法。

任務是套件中的一個物件。它會自動透過 suite.task 方法添加到當前套件中:

js
import { createTaskCollector, getCurrentSuite } 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,
  });
});
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();
  });
});
bash
vitest ./garden/tasks.test.js
Pager
上一頁插件 API
下一頁報告器 API

以 MIT 授權條款 發布。

版權所有 (c) 2021-Present Vitest Team

https://vitest.dev/advanced/runner

以 MIT 授權條款 發布。

版權所有 (c) 2021-Present Vitest Team