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

简介

为什么选择 Vitest

快速入门

特性

配置 Vitest

API

测试 API 参考

模拟函数

Vi

expect

expectTypeOf

assert

assertType

指南

命令行界面

测试过滤

测试项目

报告器

覆盖率

快照

模拟

并行

类型测试

Vitest UI

源内测试

测试上下文

测试注解

测试环境

扩展匹配器

IDE 集成

调试

常见错误

迁移指南

迁移到 Vitest 3.0

从 Jest 迁移

性能

分析测试性能

性能优化

浏览器模式

高级 API

与其他测试运行器的比较

页面导航

测试 API 参考 ​

以下类型定义在后续类型签名中使用:

ts
type Awaitable<T> = T | PromiseLike<T>;
type TestFunction = () => Awaitable<void>;

interface TestOptions {
  /**
   * 如果执行时间超过此值,测试将失败。
   */
  timeout?: number;
  /**
   * 测试失败时,将重试指定次数。
   *
   * @default 0
   */
  retry?: number;
  /**
   * 即使每次都失败,也会多次重复运行相同的测试。
   * 如果设置了 "retry" 选项且测试失败,它将在每个循环中用尽所有重试次数。
   * 这对于调试偶发性失败(flaky tests)非常有用。
   *
   * @default 0
   */
  repeats?: number;
}

当测试函数返回一个 Promise 时,测试运行器将等待 Promise 解析以收集异步断言。如果 Promise 被拒绝,测试将失败。

TIP

在 Jest 中,TestFunction 也可以是 (done: DoneCallback) => void 类型。如果使用这种形式,测试将不会在 done 被调用之前结束。你可以使用 async 函数实现同样的效果,请参阅迁移指南中的 Done Callback 部分。

你可以通过在函数上链式调用属性来定义选项:

ts
import { test } from 'vitest';

test.skip('skipped test', () => {
  // 某些当前会失败的逻辑
});

test.concurrent.skip('skipped concurrent test', () => {
  // 某些当前会失败的逻辑
});

你也可以将一个对象作为第二个参数提供:

ts
import { test } from 'vitest';

test('skipped test', { skip: true }, () => {
  // 某些当前会失败的逻辑
});

test('skipped concurrent test', { skip: true, concurrent: true }, () => {
  // 某些当前会失败的逻辑
});

这两种方式功能完全相同,选择哪种纯粹是个人风格偏好。

请注意,如果将超时时间作为最后一个参数提供,则不能再使用选项对象:

ts
import { test } from 'vitest';

// ✅ 这样可以
test.skip('heavy test', () => {
  // ...
}, 10_000);

// ❌ 这样不行
test(
  'heavy test',
  { skip: true },
  () => {
    // ...
  },
  10_000
);

但是,你可以在选项对象内部提供超时时间:

ts
import { test } from 'vitest';

// ✅ 这样可以
test('heavy test', { skip: true, timeout: 10_000 }, () => {
  // ...
});

test ​

  • 别名: it

test 定义了一组相关的断言。它接收测试名称和一个包含待测试断言的函数。

你可以选择提供一个超时时间(毫秒),用于指定在终止前等待的时长。默认值为 5 秒,可以通过 testTimeout 进行全局配置。

ts
import { expect, test } from 'vitest';

test('should work as expected', () => {
  expect(Math.sqrt(4)).toBe(2);
});

test.extend ​

  • 别名: it.extend

使用 test.extend 扩展测试上下文,添加自定义 fixture。这将返回一个新的 test 函数,并且它也是可扩展的,因此你可以根据需要组合更多 fixture 或覆盖现有 fixture。有关更多信息,请参阅扩展 Test Context。

ts
import { expect, test } from 'vitest';

const todos = [];
const archive = [];

const myTest = test.extend({
  todos: async ({ task }, use) => {
    todos.push(1, 2, 3);
    await use(todos);
    todos.length = 0;
  },
  archive,
});

myTest('add item', ({ todos }) => {
  expect(todos.length).toBe(3);

  todos.push(4);
  expect(todos.length).toBe(4);
});

test.skip ​

  • 别名: it.skip

如果你想跳过运行某些测试,但由于某种原因不想删除代码,可以使用 test.skip 来避免运行它们。

ts
import { assert, test } from 'vitest';

test.skip('skipped test', () => {
  // 测试已跳过,无错误
  assert.equal(Math.sqrt(4), 3);
});

你也可以通过在其上下文上动态调用 skip 来跳过测试:

ts
import { assert, test } from 'vitest';

test('skipped test', context => {
  context.skip();
  // 测试已跳过,无错误
  assert.equal(Math.sqrt(4), 3);
});

从 Vitest 3.1 开始,如果条件未知,你可以将其作为第一个参数提供给 skip 方法:

ts
import { assert, test } from 'vitest';

test('skipped test', context => {
  context.skip(Math.random() < 0.5, 'optional message');
  // 测试已跳过,无错误
  assert.equal(Math.sqrt(4), 3);
});

test.skipIf ​

  • 别名: it.skipIf

在某些情况下,你可能会在不同的环境中多次运行测试,并且其中一些测试可能是环境特定的。与其用 if 语句包装测试代码,不如使用 test.skipIf 在条件为真时跳过测试。

ts
import { assert, test } from 'vitest';

const isDev = process.env.NODE_ENV === 'development';

test.skipIf(isDev)('prod only test', () => {
  // 此测试仅在生产环境中运行
});

WARNING

当使用 Vitest 作为类型检查器 时,你不能使用此语法。

test.runIf ​

  • 别名: it.runIf

与 test.skipIf 相反。

ts
import { assert, test } from 'vitest';

const isDev = process.env.NODE_ENV === 'development';

test.runIf(isDev)('dev only test', () => {
  // 此测试仅在开发环境中运行
});

WARNING

当使用 Vitest 作为类型检查器 时,你不能使用此语法。

test.only ​

  • 别名: it.only

使用 test.only 仅运行给定套件中的某些测试。这在调试时非常有用。

你可以选择提供一个超时时间(毫秒),用于指定在终止前等待的时长。默认值为 5 秒,可以通过 testTimeout 进行全局配置。

ts
import { assert, test } from 'vitest';

test.only('test', () => {
  // 只有此测试(以及其他标记为 only 的测试)会运行
  assert.equal(Math.sqrt(4), 2);
});

有时,在特定文件中运行 only 测试非常有用,可以忽略整个测试套件中的所有其他测试,这些测试会污染输出。

为此,请运行包含相关测试的特定文件:

# vitest interesting.test.ts

test.concurrent ​

  • 别名: it.concurrent

test.concurrent 标记连续的测试并行运行。它接收测试名称、一个包含待收集测试的异步函数以及一个可选的超时时间(毫秒)。

ts
import { describe, test } from 'vitest';

// 两个标记为 concurrent 的测试将并行运行
describe('suite', () => {
  test('serial test', async () => {
    /* ... */
  });
  test.concurrent('concurrent test 1', async () => {
    /* ... */
  });
  test.concurrent('concurrent test 2', async () => {
    /* ... */
  });
});

test.skip、test.only 和 test.todo 适用于并发测试。以下所有组合均有效:

ts
test.concurrent(/* ... */);
test.skip.concurrent(/* ... */); // 或 test.concurrent.skip(/* ... */)
test.only.concurrent(/* ... */); // 或 test.concurrent.only(/* ... */)
test.todo.concurrent(/* ... */); // 或 test.concurrent.todo(/* ... */)

运行并发测试时,快照(Snapshots)和断言(Assertions)必须使用来自本地测试上下文(Test Context) 的 expect,以确保检测到正确的测试。

ts
test.concurrent('test 1', async ({ expect }) => {
  expect(foo).toMatchSnapshot();
});
test.concurrent('test 2', async ({ expect }) => {
  expect(foo).toMatchSnapshot();
});

WARNING

当使用 Vitest 作为类型检查器 时,你不能使用此语法。

test.sequential ​

  • 别名: it.sequential

test.sequential 将测试标记为顺序执行。如果你想在 describe.concurrent 中或使用 --sequence.concurrent 命令行选项时按顺序运行测试,这会很有用。

ts
import { describe, test } from 'vitest';

// 使用配置选项 { sequence: { concurrent: true } }
test('concurrent test 1', async () => {
  /* ... */
});
test('concurrent test 2', async () => {
  /* ... */
});

test.sequential('sequential test 1', async () => {
  /* ... */
});
test.sequential('sequential test 2', async () => {
  /* ... */
});

// 在并发套件中
describe.concurrent('suite', () => {
  test('concurrent test 1', async () => {
    /* ... */
  });
  test('concurrent test 2', async () => {
    /* ... */
  });

  test.sequential('sequential test 1', async () => {
    /* ... */
  });
  test.sequential('sequential test 2', async () => {
    /* ... */
  });
});

test.todo ​

  • 别名: it.todo

使用 test.todo 来标记待以后实现的测试。报告中会显示这些测试的条目,以便你知道还有多少测试待实现。

ts
// 报告中将显示此测试的条目
test.todo('unimplemented test');

test.fails ​

  • 别名:it.fails

使用 test.fails 明确指示断言将失败。

ts
import { expect, test } from 'vitest';

function myAsyncFunc() {
  return new Promise(resolve => resolve(1));
}
test.fails('fail test', async () => {
  await expect(myAsyncFunc()).rejects.toBe(1);
});

WARNING

当使用 Vitest 作为类型检查器 时,你不能使用此语法。

test.each ​

  • 别名: it.each

TIP

虽然提供了 test.each 以兼容 Jest, Vitest 还提供了 test.for,它具有集成 TestContext 的额外功能。

当你需要使用不同的变量运行相同的测试时,请使用 test.each。 你可以在测试名称中使用 printf 格式化来按测试函数参数的顺序注入参数。

  • %s: 字符串
  • %d: 数字
  • %i: 整数
  • %f: 浮点值
  • %j: JSON
  • %o: 对象
  • %#: 测试用例的 0-based 索引
  • %$: 测试用例的 1-based 索引
  • %%: 单个百分号 ('%')
ts
import { expect, test } from 'vitest';

test.each([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add(%i, %i) -> %i', (a, b, expected) => {
  expect(a + b).toBe(expected);
});

// 输出如下
// ✓ add(1, 1) -> 2
// ✓ add(1, 2) -> 3
// ✓ add(2, 1) -> 3

你还可以使用 $ 前缀访问对象属性和数组元素:

ts
test.each([
  { a: 1, b: 1, expected: 2 },
  { a: 1, b: 2, expected: 3 },
  { a: 2, b: 1, expected: 3 },
])('add($a, $b) -> $expected', ({ a, b, expected }) => {
  expect(a + b).toBe(expected);
});

// 输出如下
// ✓ add(1, 1) -> 2
// ✓ add(1, 2) -> 3
// ✓ add(2, 1) -> 3

test.each([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add($0, $1) -> $2', (a, b, expected) => {
  expect(a + b).toBe(expected);
});

// 输出如下
// ✓ add(1, 1) -> 2
// ✓ add(1, 2) -> 3
// ✓ add(2, 1) -> 3

如果你使用对象作为参数,你还可以使用 . 访问对象属性:

ts
test.each`
  a             | b      | expected
  ${{ val: 1 }} | ${'b'} | ${'1b'}
  ${{ val: 2 }} | ${'b'} | ${'2b'}
  ${{ val: 3 }} | ${'b'} | ${'3b'}
`('add($a.val, $b) -> $expected', ({ a, b, expected }) => {
  expect(a.val + b).toBe(expected);
});

// 输出如下
// ✓ add(1, b) -> 1b
// ✓ add(2, b) -> 2b
// ✓ add(3, b) -> 3b

从 Vitest 0.25.3 开始,你还可以使用模板字符串表格。

  • 第一行应为列名,用 | 分隔;
  • 随后的一行或多行数据作为模板字面量表达式提供,使用 ${value} 语法。
ts
import { expect, test } from 'vitest';

test.each`
  a             | b      | expected
  ${1}          | ${1}   | ${2}
  ${'a'}        | ${'b'} | ${'ab'}
  ${[]}         | ${'b'} | ${'b'}
  ${{}}         | ${'b'} | ${'[object Object]b'}
  ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'}
`('returns $expected when $a is added $b', ({ a, b, expected }) => {
  expect(a + b).toBe(expected);
});

TIP

Vitest 使用 Chai 的 format 方法处理 $values。如果值被截断过多,你可以在配置文件中增加 chaiConfig.truncateThreshold。

WARNING

当使用 Vitest 作为类型检查器 时,你不能使用此语法。

test.for ​

  • 别名: it.for

test.each 的替代方案,用于提供 TestContext。

test.for 与 test.each 的区别在于参数中数组的提供方式。 test.for 的非数组参数(包括模板字符串用法)与 test.each 的工作方式完全相同。

ts
// `each` 展开数组
test.each([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add(%i, %i) -> %i', (a, b, expected) => { 
  expect(a + b).toBe(expected);
});

// `for` 不展开数组(注意参数周围的方括号)
test.for([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add(%i, %i) -> %i', ([a, b, expected]) => { 
  expect(a + b).toBe(expected);
});

第二个参数是 TestContext,例如可用于并发快照:

ts
test.concurrent.for([
  [1, 1],
  [1, 2],
  [2, 1],
])('add(%i, %i)', ([a, b], { expect }) => {
  expect(a + b).matchSnapshot();
});

bench ​

  • 类型: (name: string | Function, fn: BenchFunction, options?: BenchOptions) => void

bench 定义了一个基准测试。在 Vitest 中,基准测试是一个定义一系列操作的函数。Vitest 会多次运行此函数以显示不同的性能结果。

Vitest 内部使用 tinybench 库,继承了其所有选项,这些选项可以用作第三个参数。

ts
import { bench } from 'vitest';

bench(
  'normal sorting',
  () => {
    const x = [1, 5, 4, 2, 3];
    x.sort((a, b) => {
      return a - b;
    });
  },
  { time: 1000 }
);
ts
export interface Options {
  /**
   * 运行基准测试任务所需的时间(毫秒)
   * @default 500
   */
  time?: number;

  /**
   * 即使时间选项已完成,任务也应运行的次数
   * @default 10
   */
  iterations?: number;

  /**
   * 获取当前时间戳(毫秒)的函数
   */
  now?: () => number;

  /**
   * 用于中止基准测试的 `AbortSignal`
   */
  signal?: AbortSignal;

  /**
   * 如果任务失败则抛出(如果为 true,事件将不起作用)
   */
  throws?: boolean;

  /**
   * 热身时间(毫秒)
   * @default 100ms
   */
  warmupTime?: number;

  /**
   * 热身迭代次数
   * @default 5
   */
  warmupIterations?: number;

  /**
   * 在每个基准测试任务(循环)之前运行的设置函数
   */
  setup?: Hook;

  /**
   * 在每个基准测试任务(循环)之后运行的拆卸函数
   */
  teardown?: Hook;
}

测试用例运行后,输出结构信息如下:

  name                      hz     min     max    mean     p75     p99    p995    p999     rme  samples
· normal sorting  6,526,368.12  0.0001  0.3638  0.0002  0.0002  0.0002  0.0002  0.0004  ±1.41%   652638
ts
export interface TaskResult {
  /*
   * 运行任务时抛出的最后一个错误
   */
  error?: unknown;

  /**
   * 运行基准测试任务(循环)所需的时间(毫秒)。
   */
  totalTime: number;

  /**
   * 样本中的最小值
   */
  min: number;
  /**
   * 样本中的最大值
   */
  max: number;

  /**
   * 每秒操作数
   */
  hz: number;

  /**
   * 每次操作所需的时间(毫秒)
   */
  period: number;

  /**
   * 每个任务迭代时间的任务样本(毫秒)
   */
  samples: number[];

  /**
   * 样本均值/平均值(总体均值的估计)
   */
  mean: number;

  /**
   * 样本方差(总体方差的估计)
   */
  variance: number;

  /**
   * 样本标准差(总体标准差的估计)
   */
  sd: number;

  /**
   * 均值的标准误差(又称样本均值抽样分布的标准差)
   */
  sem: number;

  /**
   * 自由度
   */
  df: number;

  /**
   * 样本的临界值
   */
  critical: number;

  /**
   * 误差范围
   */
  moe: number;

  /**
   * 相对误差范围
   */
  rme: number;

  /**
   * 中位数绝对偏差
   */
  mad: number;

  /**
   * p50/中位数百分位数
   */
  p50: number;

  /**
   * p75 百分位数
   */
  p75: number;

  /**
   * p99 百分位数
   */
  p99: number;

  /**
   * p995 百分位数
   */
  p995: number;

  /**
   * p999 百分位数
   */
  p999: number;
}

bench.skip ​

  • 类型: (name: string | Function, fn: BenchFunction, options?: BenchOptions) => void

你可以使用 bench.skip 语法来跳过运行某些基准测试。

ts
import { bench } from 'vitest';

bench.skip('normal sorting', () => {
  const x = [1, 5, 4, 2, 3];
  x.sort((a, b) => {
    return a - b;
  });
});

bench.only ​

  • 类型: (name: string | Function, fn: BenchFunction, options?: BenchOptions) => void

使用 bench.only 仅运行给定套件中的某些基准测试。这在调试时非常有用。

ts
import { bench } from 'vitest';

bench.only('normal sorting', () => {
  const x = [1, 5, 4, 2, 3];
  x.sort((a, b) => {
    return a - b;
  });
});

bench.todo ​

  • 类型: (name: string | Function) => void

使用 bench.todo 来标记待以后实现的基准测试。

ts
import { bench } from 'vitest';

bench.todo('unimplemented test');

describe ​

当你在文件的顶层使用 test 或 bench 时,它们会被收集为隐式套件的一部分。使用 describe,你可以在当前上下文中定义一个新套件,作为一组相关的测试或基准测试以及其他嵌套套件。套件允许你组织测试和基准测试,使报告更清晰。

ts
// basic.spec.ts
// 组织测试

import { describe, expect, test } from 'vitest';

const person = {
  isActive: true,
  age: 32,
};

describe('person', () => {
  test('person is defined', () => {
    expect(person).toBeDefined();
  });

  test('is active', () => {
    expect(person.isActive).toBeTruthy();
  });

  test('age limit', () => {
    expect(person.age).toBeLessThanOrEqual(32);
  });
});
ts
// basic.bench.ts
// 组织基准测试

import { bench, describe } from 'vitest';

describe('sort', () => {
  bench('normal', () => {
    const x = [1, 5, 4, 2, 3];
    x.sort((a, b) => {
      return a - b;
    });
  });

  bench('reverse', () => {
    const x = [1, 5, 4, 2, 3];
    x.reverse().sort((a, b) => {
      return a - b;
    });
  });
});

如果你有测试或基准测试的层次结构,你也可以嵌套 describe 块:

ts
import { describe, expect, test } from 'vitest';

function numberToCurrency(value: number | string) {
  if (typeof value !== 'number') {
    throw new TypeError('Value must be a number');
  }

  return value
    .toFixed(2)
    .toString()
    .replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}

describe('numberToCurrency', () => {
  describe('given an invalid number', () => {
    test('composed of non-numbers to throw error', () => {
      expect(() => numberToCurrency('abc')).toThrowError();
    });
  });

  describe('given a valid number', () => {
    test('returns the correct currency format', () => {
      expect(numberToCurrency(10000)).toBe('10,000.00');
    });
  });
});

describe.skip ​

  • 别名: suite.skip

在套件中使用 describe.skip 可以避免运行特定的 describe 块。

ts
import { assert, describe, test } from 'vitest';

describe.skip('skipped suite', () => {
  test('sqrt', () => {
    // 套件已跳过,无错误
    assert.equal(Math.sqrt(4), 3);
  });
});

describe.skipIf ​

  • 别名: suite.skipIf

在某些情况下,你可能会在不同的环境中多次运行套件,并且其中一些套件可能是环境特定的。与其用 if 语句包装套件,不如使用 describe.skipIf 在条件为真时跳过套件。

ts
import { describe, test } from 'vitest';

const isDev = process.env.NODE_ENV === 'development';

describe.skipIf(isDev)('prod only test suite', () => {
  // 此测试套件仅在生产环境中运行
});

WARNING

当使用 Vitest 作为类型检查器 时,你不能使用此语法。

describe.runIf ​

  • 别名: suite.runIf

与 describe.skipIf 相反。

ts
import { assert, describe, test } from 'vitest';

const isDev = process.env.NODE_ENV === 'development';

describe.runIf(isDev)('dev only test suite', () => {
  // 此测试套件仅在开发环境中运行
});

WARNING

当使用 Vitest 作为类型检查器 时,你不能使用此语法。

describe.only ​

  • 类型: (name: string | Function, fn: TestFunction, options?: number | TestOptions) => void

使用 describe.only 仅运行某些套件。

ts
import { assert, describe, test } from 'vitest';

// 只有此套件(以及其他标记为 only 的套件)会运行
describe.only('suite', () => {
  test('sqrt', () => {
    assert.equal(Math.sqrt(4), 3);
  });
});

describe('other suite', () => {
  // ... 将被跳过
});

有时,在特定文件中运行 only 测试非常有用,可以忽略整个测试套件中的所有其他测试,这些测试会污染输出。

为此,请运行包含相关测试的特定文件:

# vitest interesting.test.ts

describe.concurrent ​

  • 别名: suite.concurrent

describe.concurrent 并行运行所有内部套件和测试。

ts
import { describe, test } from 'vitest';

// 此套件中的所有套件和测试都将并行运行
describe.concurrent('suite', () => {
  test('concurrent test 1', async () => {
    /* ... */
  });
  describe('concurrent suite 2', async () => {
    test('concurrent test inner 1', async () => {
      /* ... */
    });
    test('concurrent test inner 2', async () => {
      /* ... */
    });
  });
  test.concurrent('concurrent test 3', async () => {
    /* ... */
  });
});

.skip、.only 和 .todo 适用于并发套件。以下所有组合均有效:

ts
describe.concurrent(/* ... */);
describe.skip.concurrent(/* ... */); // 或 describe.concurrent.skip(/* ... */)
describe.only.concurrent(/* ... */); // 或 describe.concurrent.only(/* ... */)
describe.todo.concurrent(/* ... */); // 或 describe.concurrent.todo(/* ... */)

运行并发测试时,快照(Snapshots)和断言(Assertions)必须使用来自本地测试上下文(Test Context) 的 expect,以确保检测到正确的测试。

ts
describe.concurrent('suite', () => {
  test('concurrent test 1', async ({ expect }) => {
    expect(foo).toMatchSnapshot();
  });
  test('concurrent test 2', async ({ expect }) => {
    expect(foo).toMatchSnapshot();
  });
});

WARNING

当使用 Vitest 作为类型检查器 时,你不能使用此语法。

describe.sequential ​

  • 别名: suite.sequential

套件中的 describe.sequential 将每个测试标记为顺序执行。如果你想在 describe.concurrent 中或使用 --sequence.concurrent 命令行选项时按顺序运行测试,这会很有用。

ts
import { describe, test } from 'vitest';

describe.concurrent('suite', () => {
  test('concurrent test 1', async () => {
    /* ... */
  });
  test('concurrent test 2', async () => {
    /* ... */
  });

  describe.sequential('', () => {
    test('sequential test 1', async () => {
      /* ... */
    });
    test('sequential test 2', async () => {
      /* ... */
    });
  });
});

describe.shuffle ​

  • 别名: suite.shuffle

Vitest 提供了一种通过 CLI 标志 --sequence.shuffle 或配置选项 sequence.shuffle 以随机顺序运行所有测试的方法,但如果你只想让测试套件的一部分以随机顺序运行测试,你可以使用此标志进行标记。

ts
import { describe, test } from 'vitest';

// 或 describe('suite', { shuffle: true }, ...)
describe.shuffle('suite', () => {
  test('random test 1', async () => {
    /* ... */
  });
  test('random test 2', async () => {
    /* ... */
  });
  test('random test 3', async () => {
    /* ... */
  });

  // `shuffle` 是继承的
  describe('still random', () => {
    test('random 4.1', async () => {
      /* ... */
    });
    test('random 4.2', async () => {
      /* ... */
    });
  });

  // 禁用内部的 shuffle
  describe('not random', { shuffle: false }, () => {
    test('in order 5.1', async () => {
      /* ... */
    });
    test('in order 5.2', async () => {
      /* ... */
    });
  });
});
// 顺序取决于配置中的 `sequence.seed` 选项(默认为 `Date.now()`)

.skip、.only 和 .todo 适用于随机套件。

WARNING

当使用 Vitest 作为类型检查器 时,你不能使用此语法。

describe.todo ​

  • 别名: suite.todo

使用 describe.todo 来标记待以后实现的套件。报告中会显示这些套件的条目,以便你知道还有多少套件待实现。

ts
// 报告中将显示此套件的条目
describe.todo('unimplemented suite');

describe.each ​

  • 别名: suite.each

TIP

虽然提供了 describe.each 以兼容 Jest, Vitest 还提供了 describe.for,它简化了参数类型并与 test.for 对齐。

如果你有多个测试依赖于相同的数据,请使用 describe.each。

ts
import { describe, expect, test } from 'vitest';

describe.each([
  { a: 1, b: 1, expected: 2 },
  { a: 1, b: 2, expected: 3 },
  { a: 2, b: 1, expected: 3 },
])('describe object add($a, $b)', ({ a, b, expected }) => {
  test(`returns ${expected}`, () => {
    expect(a + b).toBe(expected);
  });

  test(`returned value not be greater than ${expected}`, () => {
    expect(a + b).not.toBeGreaterThan(expected);
  });

  test(`returned value not be less than ${expected}`, () => {
    expect(a + b).not.toBeLessThan(expected);
  });
});

从 Vitest 0.25.3 开始,你还可以使用模板字符串表格。

  • 第一行应为列名,用 | 分隔;
  • 随后的一行或多行数据作为模板字面量表达式提供,使用 ${value} 语法。
ts
import { describe, expect, test } from 'vitest';

describe.each`
  a             | b      | expected
  ${1}          | ${1}   | ${2}
  ${'a'}        | ${'b'} | ${'ab'}
  ${[]}         | ${'b'} | ${'b'}
  ${{}}         | ${'b'} | ${'[object Object]b'}
  ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'}
`('describe template string add($a, $b)', ({ a, b, expected }) => {
  test(`returns ${expected}`, () => {
    expect(a + b).toBe(expected);
  });
});

WARNING

当使用 Vitest 作为类型检查器 时,你不能使用此语法。

describe.for ​

  • 别名: suite.for

与 describe.each 的区别在于参数中数组用例的提供方式。 其他非数组用例(包括模板字符串用法)的工作方式完全相同。

ts
// `each` 展开数组用例
describe.each([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add(%i, %i) -> %i', (a, b, expected) => { 
  test('test', () => {
    expect(a + b).toBe(expected);
  });
});

// `for` 不展开数组用例
describe.for([
  [1, 1, 2],
  [1, 2, 3],
  [2, 1, 3],
])('add(%i, %i) -> %i', ([a, b, expected]) => { 
  test('test', () => {
    expect(a + b).toBe(expected);
  });
});

设置和拆卸 ​

这些函数允许你钩入测试的生命周期中,以避免重复设置和拆卸代码。它们适用于当前上下文:如果它们在顶层使用,则适用于文件;如果它们在 describe 块内部,则适用于当前套件。当你将 Vitest 作为类型检查器运行时,这些钩子不会被调用。

beforeEach ​

  • 类型: beforeEach(fn: () => Awaitable<void>, timeout?: number)

注册一个回调函数,在当前上下文中每个测试运行之前调用。 如果函数返回一个 Promise,Vitest 会等待 Promise 解析后再运行测试。

你可以选择传递一个超时时间(毫秒),定义在终止前等待的时长。默认值为 5 秒。

ts
import { beforeEach } from 'vitest';

beforeEach(async () => {
  // 在每次测试运行前清除模拟并添加一些测试数据
  await stopMocking();
  await addUser({ name: 'John' });
});

在这里,beforeEach 确保在每次测试之前都添加了用户。

beforeEach 也接受一个可选的清理函数(等同于 afterEach)。

ts
import { beforeEach } from 'vitest';

beforeEach(async () => {
  // 在每次测试运行前调用一次
  await prepareSomething();

  // 清理函数,在每次测试运行后调用一次
  return async () => {
    await resetSomething();
  };
});

afterEach ​

  • 类型: afterEach(fn: () => Awaitable<void>, timeout?: number)

注册一个回调函数,在当前上下文中每个测试完成后调用。 如果函数返回一个 Promise,Vitest 会等待 Promise 解析后再继续。

你可以选择提供一个超时时间(毫秒),用于指定在终止前等待的时长。默认值为 5 秒。

ts
import { afterEach } from 'vitest';

afterEach(async () => {
  await clearTestingData(); // 在每次测试运行后清除测试数据
});

在这里,afterEach 确保在每次测试运行后清除测试数据。

TIP

Vitest 1.3.0 添加了 onTestFinished 钩子。你可以在测试执行期间调用它,以便在测试运行结束后清理任何状态。

beforeAll ​

  • 类型: beforeAll(fn: () => Awaitable<void>, timeout?: number)

注册一个回调函数,在当前上下文中所有测试开始运行之前调用一次。 如果函数返回一个 Promise,Vitest 会等待 Promise 解析后再运行测试。

你可以选择提供一个超时时间(毫秒),用于指定在终止前等待的时长。默认值为 5 秒。

ts
import { beforeAll } from 'vitest';

beforeAll(async () => {
  await startMocking(); // 在所有测试运行前调用一次
});

在这里,beforeAll 确保在测试运行之前设置好模拟数据。

beforeAll 也接受一个可选的清理函数(等同于 afterAll)。

ts
import { beforeAll } from 'vitest';

beforeAll(async () => {
  // 在所有测试运行前调用一次
  await startMocking();

  // 清理函数,在所有测试运行后调用一次
  return async () => {
    await stopMocking();
  };
});

afterAll ​

  • 类型: afterAll(fn: () => Awaitable<void>, timeout?: number)

注册一个回调函数,在当前上下文中所有测试运行完毕后调用一次。 如果函数返回一个 Promise,Vitest 会等待 Promise 解析后再继续。

你可以选择提供一个超时时间(毫秒),用于指定在终止前等待的时长。默认值为 5 秒。

ts
import { afterAll } from 'vitest';

afterAll(async () => {
  await stopMocking(); // 此方法在所有测试运行后调用
});

在这里,afterAll 确保在所有测试运行后调用 stopMocking 方法。

测试钩子 ​

Vitest 提供了一些钩子,你可以在测试执行_期间_调用它们,以便在测试运行结束后清理状态。

WARNING

如果在测试体之外调用这些钩子,它们将抛出错误。

onTestFinished ​

此钩子总是在测试运行结束后调用。它在 afterEach 钩子之后调用,因为它们可能会影响测试结果。它接收一个 ExtendedContext 对象,就像 beforeEach 和 afterEach 一样。

ts
import { onTestFinished, test } from 'vitest';

test('performs a query', () => {
  const db = connectDb();
  onTestFinished(() => db.close());
  db.query('SELECT * FROM users');
});

WARNING

如果你正在并发运行测试,你应该始终使用测试上下文中的 onTestFinished 钩子,因为 Vitest 不会在全局钩子中跟踪并发测试:

ts
import { test } from 'vitest';

test.concurrent('performs a query', ({ onTestFinished }) => {
  const db = connectDb();
  onTestFinished(() => db.close());
  db.query('SELECT * FROM users');
});

此钩子在创建可重用逻辑时特别有用:

ts
// 这可以放在单独的文件中
function getTestDb() {
  const db = connectMockedDb();
  onTestFinished(() => db.close());
  return db;
}

test('performs a user query', async () => {
  const db = getTestDb();
  expect(await db.query('SELECT * from users').perform()).toEqual([]);
});

test('performs an organization query', async () => {
  const db = getTestDb();
  expect(await db.query('SELECT * from organizations').perform()).toEqual([]);
});

TIP

此钩子总是以相反的顺序调用,并且不受 sequence.hooks 选项的影响。

onTestFailed ​

此钩子仅在测试失败后调用。它在 afterEach 钩子之后调用,因为它们可能会影响测试结果。它接收一个 ExtendedContext 对象,就像 beforeEach 和 afterEach 一样。此钩子对于调试很有用。

ts
import { onTestFailed, test } from 'vitest';

test('performs a query', () => {
  const db = connectDb();
  onTestFailed(({ task }) => {
    console.log(task.result.errors);
  });
  db.query('SELECT * FROM users');
});

WARNING

如果你正在并发运行测试,你应该始终使用测试上下文中的 onTestFailed 钩子,因为 Vitest 不会在全局钩子中跟踪并发测试:

ts
import { test } from 'vitest';

test.concurrent('performs a query', ({ onTestFailed }) => {
  const db = connectDb();
  onTestFailed(({ task }) => {
    console.log(task.result.errors);
  });
  db.query('SELECT * FROM users');
});
Pager
上一页配置 Vitest
下一页模拟函数

基于 MIT 许可证 发布。

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

https://vitest.dev/api/

基于 MIT 许可证 发布。

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