Skip to content
Vitest 2
Main Navigation 指南API配置浏览器模式高级
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

Node API

测试运行器

任务元数据

扩展报告器

自定义 pool

页面导航

测试运行器 ​

WARNING

这是高级 API。如果你只是想运行测试,可能不需要使用它。它主要供库作者使用。

你可以在配置文件中使用 runner 选项指定测试运行器的路径。该文件应有一个默认导出,导出一个实现以下方法的类构造函数:

ts
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>;

  /**
   * 当任务更新时调用。与 reporter 中的 "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>;
  /**
   * 当导入某些文件时调用。可以在两种情况下调用:收集测试时和导入 setup 文件时。
   */
  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 属性可用。

ts
interface File extends Suite {
  /**
   * 文件所属的池的名称。
   * @default 'forks'
   */
  pool?: string;
  /**
   * 文件路径,采用 UNIX 格式。
   */
  filepath: string;
  /**
   * 文件所属的工作区项目的名称。
   */
  projectName: string | undefined;
  /**
   * 收集文件中所有测试所花费的时间。
   * 此时间也包括导入所有文件依赖项的时间。
   */
  collectDuration?: number;
  /**
   * 导入 setup 文件所花费的时间。
   */
  setupDuration?: number;
  /**
   * 文件是否在未运行任何测试的情况下初始化。
   * 这是为了让 Vitest 在服务器端填充状态。
   */
  local?: boolean;
}

每个套件都有一个 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: TaskContext<Test> & ExtraContext & TestContext;
  /**
   * 文件任务。它是文件的根任务。
   */
  file: File;
  /**
   * 任务是否因调用 `t.skip()` 而被跳过。
   */
  pending?: boolean;
  /**
   * 任务失败时是否应视为成功。如果任务失败,它将被标记为通过。
   */
  fails?: boolean;
  /**
   * 如果任务失败将运行的钩子。顺序取决于 `sequence.hooks` 选项。
   */
  onFailed?: OnTestFailedHandler[];
  /**
   * 任务完成后将运行的钩子。顺序取决于 `sequence.hooks` 选项。
   */
  onFinished?: OnTestFinishedHandler[];
  /**
   * 存储 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 提供了一个 Custom 任务类型,允许用户重用内置的 reporter。它实际上与 Test 相同,但类型为 'custom'。

任务是套件中的一个对象。它会被自动添加到当前套件中,使用 suite.task 方法:

js
// ./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,
  });
});
js
// ./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();
  });
});
bash
vitest ./garden/tasks.test.js

WARNING

如果你没有自定义运行器或未定义 runTest 方法,Vitest 将尝试自动检索任务。如果你没有使用 setFn 添加函数,操作将会失败。

Pager
上一页Node API
下一页任务元数据

基于 MIT 许可证 发布。

版权所有 (c) 2024 Mithril Contributors

https://v2.vitest.dev/advanced/runner

基于 MIT 许可证 发布。

版权所有 (c) 2024 Mithril Contributors