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

Runner API

报告器 API

任务元数据

指南

运行测试

扩展报告器

自定义测试池

配置 Vitest

测试 API 参考

页面导航

Runner API ​

WARNING

这是一个高级 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>;

  /**
   * 当任务更新时调用。与 reporter 中的 `onTaskUpdate` 作用相同,但此方法在与测试相同的线程中运行。
   */
  onTaskUpdate?: (
    task: [string, TaskResult | undefined, TaskMeta | undefined][]
  ) => Promise<void>;

  /**
   * 在运行收集到的路径中的所有测试之前调用。
   */
  onBeforeRunFiles?: (files: File[]) => unknown;
  /**
   * 在运行收集到的路径中的所有测试之后立即调用。
   */
  onAfterRunFiles?: (files: File[]) => unknown;
  /**
   * 当定义新的测试上下文时调用。如果你想向该上下文添加自定义属性,此方法将非常有用。
   * 如果你只想通过 runner 定义自定义上下文,请考虑改用 `setupFiles` 中的 `beforeAll`。
   */
  extendTaskContext?: (context: TestContext) => TestContext;
  /**
   * 当导入某些文件时调用。可在两种情况下调用:收集测试和导入设置文件。
   */
  importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
  /**
   * 当 `test.extend` 与 `{ injected: true }` 一起使用时,runner 尝试获取值时调用的函数。
   */
  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

“Runner Tasks API”是实验性的,主要应仅在测试运行时使用。Vitest 还暴露了“Reported Tasks API”,在主线程中工作时(例如在 reporter 内部)应优先使用此 API。

团队目前正在讨论未来是否应以“Reported Tasks”替换“Runner Tasks”。

套件和测试在内部统称为 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(来自异步 expect),以便在测试完成前等待它们。
   */
  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