测试 API 索引
以下类型用于下面的类型签名中
type Awaitable<T> = T | PromiseLike<T>;
type TestFunction = () => Awaitable<void>;
interface TestOptions {
/**
* 如果测试执行时间超过此时间,测试将会失败
*/
timeout?: number;
/**
* 如果测试失败,将会重试指定的次数
*
* @default 0
*/
retry?: number;
/**
* 即使每次都失败,仍会多次重复运行相同的测试
* 如果您设置了 "retry" 选项,且测试失败,则每次重试都会在每个循环周期中执行。
* 用于调试偶发性失败
*
* @default 0
*/
repeats?: number;
}当测试函数返回一个 Promise 时,运行器会等待其 resolve 以收集异步断言结果。如果 Promise 被 reject,测试将失败。
TIP
在 Jest 中,TestFunction 也可以是 (done: DoneCallback) => void 类型。如果使用这种形式,测试将不会结束,直到 done 被调用。您可以使用 async 函数实现相同的效果,请参阅 迁移指南 Done Callback 章节。
test
类型:
(name: string|Function, fn: TestFunction, timeout?: number|TestOptions) => void别名:
ittest定义了一组相关的期望。它接受测试名称和一个包含期望的函数。可选地,您可以提供一个超时时间(以毫秒为单位),以指定在终止之前等待的最长时间。默认值为 5 秒,并且可以使用 testTimeout 进行全局配置。
tsimport { expect, test } from 'vitest'; test('should work as expected', () => { expect(Math.sqrt(4)).toBe(2); });
test.extend
类型:
<T extends Record<string, any>>(fixtures: Fixtures<T>): TestAPI<ExtraContext & T>别名:
it.extend版本: Vitest 0.32.3
使用
test.extend可以使用自定义的 fixtures 扩展测试上下文。这将返回一个新的test实例,该实例也是可扩展的。因此,您可以根据需要通过扩展它来组合或覆盖现有的 fixtures。有关更多信息,请参见 扩展测试上下文。tsimport { 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
类型:
(name: string | Function, fn: TestFunction, timeout?: number | TestOptions) => void别名:
it.skip如果您想跳过某些测试的运行,但又不想删除代码,则可以使用
test.skip来避免运行它们。tsimport { assert, test } from 'vitest'; test.skip('skipped test', () => { // 测试被跳过,没有错误 assert.equal(Math.sqrt(4), 3); });您还可以通过在其 context 上动态调用
skip来跳过测试:tsimport { assert, test } from 'vitest'; test('skipped test', context => { context.skip(); // 测试被跳过,没有错误 assert.equal(Math.sqrt(4), 3); });
test.skipIf
类型:
(condition: any) => Test别名:
it.skipIf在某些情况下,您可能会使用不同的环境多次运行测试,并且某些测试可能是特定于环境的。您可以不使用
if语句包裹测试代码,而是使用test.skipIf,当条件为真时跳过测试。tsimport { assert, test } from 'vitest'; const isDev = process.env.NODE_ENV === 'development'; test.skipIf(isDev)('prod only test', () => { // 此测试仅在生产环境中运行 });
WARNING
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
test.runIf
类型:
(condition: any) => Test别名:
it.runIf与 test.skipIf 的功能相反。
tsimport { assert, test } from 'vitest'; const isDev = process.env.NODE_ENV === 'development'; test.runIf(isDev)('dev only test', () => { // 此测试仅在开发环境中运行 });
WARNING
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
test.only
类型:
(name: string | Function, fn: TestFunction, timeout?: number) => void别名:
it.only使用
test.only仅运行给定套件中的某些测试。这在调试时非常有用。可选地,您可以提供一个超时时间(以毫秒为单位),以指定在终止之前等待的最长时间。默认值为 5 秒,并且可以使用 testTimeout 进行全局配置。
tsimport { assert, test } from 'vitest'; test.only('test', () => { // 仅运行此测试(以及其他标记为 only 的测试) assert.equal(Math.sqrt(4), 2); });有时,只运行某个文件中的
only测试非常有用,可以忽略整个测试套件中的其他测试,避免输出信息混淆。为了做到这一点,请使用包含相关测试的特定文件运行
vitest。# vitest interesting.test.ts
test.concurrent
类型:
(name: string | Function, fn: TestFunction, timeout?: number) => void别名:
it.concurrenttest.concurrent用于标记需要并行运行的测试。它接收测试名称、一个包含要收集的测试的异步函数和一个可选的超时时间(以毫秒为单位)。tsimport { 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可以与并发测试一起使用。以下所有组合都是有效的:tstest.concurrent(/* ... */); test.skip.concurrent(/* ... */); // or test.concurrent.skip(/* ... */) test.only.concurrent(/* ... */); // or test.concurrent.only(/* ... */) test.todo.concurrent(/* ... */); // or test.concurrent.todo(/* ... */)当运行并发测试时,快照和断言必须使用来自本地 测试上下文 的
expect,以确保检测到正确的测试。tstest.concurrent('test 1', async ({ expect }) => { expect(foo).toMatchSnapshot(); }); test.concurrent('test 2', async ({ expect }) => { expect(foo).toMatchSnapshot(); });
WARNING
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
test.todo
类型:
(name: string | Function) => void别名:
it.todo使用
test.todo来标记待实现的测试。测试报告中会显示这些条目,方便您了解还有多少测试需要实现。ts// 报告中将显示此测试的条目 test.todo('unimplemented test');
test.fails
类型:
(name: string | Function, fn: TestFunction, timeout?: number) => void别名:
it.fails使用
test.fails来指示断言将显式失败。tsimport { 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
类型:
(cases: ReadonlyArray<T>, ...args: any[]) => void别名:
it.each当您需要使用不同的变量多次运行相同的测试时,请使用
test.each。 您可以使用 printf 格式 ,按照测试函数参数的顺序,在测试名称中注入参数。%s: 字符串%d: 数字%i: 整数%f: 浮点值%j: JSON%o: 对象%#: 测试用例的索引%%: 单个百分号 ('%')
tstest.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当使用对象作为参数时,您还可以使用
$前缀访问对象属性:tstest.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当使用对象作为参数时,您还可以使用
.访问对象属性:tstest.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}语法的模板字面量表达式提供。
tstest.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); });如果您想访问
TestContext,请将describe.each与单个测试一起使用。
TIP
Vitest 使用 chai format 方法处理 $values。如果值被截断得太多,您可以在配置文件中增加 chaiConfig.truncateThreshold。
WARNING
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
bench
- 类型:
(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void
bench 定义一个基准测试。在 Vitest 中,基准测试是指定义了一系列操作的函数。Vitest 会多次运行此函数,以展示不同的性能指标。
Vitest 在底层使用 tinybench 库,继承了所有可用作第三个参数的选项。
import { bench } from 'vitest';
bench(
'normal sorting',
() => {
const x = [1, 5, 4, 2, 3];
x.sort((a, b) => {
return a - b;
});
},
{ time: 1000 }
);export interface Options {
/**
* 执行基准测试任务的时间限制(毫秒)
* @default 500
*/
time?: number;
/**
* 即使时间选项已完成,任务也应运行的次数
* @default 10
*/
iterations?: number;
/**
* 获取当前时间戳(以毫秒为单位)的函数
*/
now?: () => number;
/**
* 用于中止基准测试的 AbortSignal
*/
signal?: AbortSignal;
/**
* 预热时间(毫秒)
* @default 100ms
*/
warmupTime?: number;
/**
* 预热迭代次数
* @default 5
*/
warmupIterations?: number;
/**
* 在每个基准测试任务(周期)之前运行的 setup 函数
*/
setup?: Hook;
/**
* 在每个基准测试任务(周期)之后运行的 teardown 函数
*/
teardown?: Hook;
}bench.skip
- 类型:
(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void
您可以使用 bench.skip 来跳过某些基准测试的运行。
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 可以仅运行指定测试套件中的某些基准测试,这在调试时非常有用。
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 来存根以后要实现的基准测试。
import { bench } from 'vitest';
bench.todo('unimplemented test');describe
当你在文件的顶层使用 test 或 bench 时,它们会被收集为该文件的默认测试套件。使用 describe 你可以在当前上下文中定义一个新的测试套件,用于组织一组相关的测试、基准测试或其他嵌套套件。一个测试套件可以让你更好地组织测试和基准测试,使报告更加清晰。
// 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);
});
});// 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 代码块:
import { describe, expect, test } from 'vitest';
function numberToCurrency(value) {
if (typeof value !== 'number') throw new Error('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
类型:
(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void在测试套件中使用
describe.skip可以跳过特定的describe代码块。tsimport { assert, describe, test } from 'vitest'; describe.skip('skipped suite', () => { test('sqrt', () => { // 套件已跳过,不会报错 assert.equal(Math.sqrt(4), 3); }); });
describe.skipIf
类型:
(condition: any) => void在某些情况下,你可能会在不同的环境多次运行测试套件,并且某些套件可能是特定于某个环境的。你可以使用
describe.skipIf跳过测试套件,而无需使用if语句包裹,只要条件为真值(truthy)就会跳过。tsimport { describe, test } from 'vitest'; const isDev = process.env.NODE_ENV === 'development'; describe.skipIf(isDev)('prod only test', () => { // 这个测试只在生产环境中运行 });
WARNING
当使用 Vitest 作为类型检查器时,你不能使用此语法。
describe.only
类型:
(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void使用
describe.only仅运行特定的测试套件。ts// 仅运行此套件(以及其他标记为 only 的套件) describe.only('suite', () => { test('sqrt', () => { assert.equal(Math.sqrt(4), 3); }); }); describe('other suite', () => { // ... 将被跳过 });有时,在特定文件中运行
only测试非常方便,可以忽略整个测试套件中的其他测试,避免输出信息冗余。为了做到这一点,请使用包含相关测试的特定文件运行
vitest。# vitest interesting.test.ts
describe.concurrent
类型:
(name: string | Function, fn: TestFunction, options?: number | TestOptions) => voiddescribe.concurrent会将测试套件中的每个测试标记为并发执行。ts// 此套件中的所有测试都将并行运行 describe.concurrent('suite', () => { test('concurrent test 1', async () => { /* ... */ }); test('concurrent test 2', async () => { /* ... */ }); test.concurrent('concurrent test 3', async () => { /* ... */ }); });.skip、.only和.todo可以与并发套件一起使用。以下所有组合都是有效的:tsdescribe.concurrent(/* ... */); describe.skip.concurrent(/* ... */); // or describe.concurrent.skip(/* ... */) describe.only.concurrent(/* ... */); // or describe.concurrent.only(/* ... */) describe.todo.concurrent(/* ... */); // or describe.concurrent.todo(/* ... */)
当运行并发测试时,快照和断言必须使用来自本地测试上下文的 expect,以确保检测到正确的测试。
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
类型:
(name: string | Function, fn: TestFunction, options?: number | TestOptions) => voiddescribe.sequential会将测试套件中的每个测试标记为顺序执行。如果你希望在describe.concurrent内部,或者使用--sequence.concurrent命令行选项来顺序运行测试,这将非常有用。tsdescribe.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
类型:
(name: string | Function, fn: TestFunction, options?: number | TestOptions) => voidVitest 提供了通过 CLI 标志
--sequence.shuffle或配置选项sequence.shuffle以随机顺序运行所有测试的方式。但如果你只想让测试套件中的一部分以随机顺序运行,可以使用此标志标记。tsdescribe.shuffle('suite', () => { test('random test 1', async () => { /* ... */ }); test('random test 2', async () => { /* ... */ }); test('random test 3', async () => { /* ... */ }); }); // 顺序取决于配置中的 sequence.seed 选项(默认为 Date.now())
.skip、.only 和 .todo 可以与随机套件一起使用。
WARNING
当使用 Vitest 作为类型检查器时,你不能使用此语法。
describe.todo
类型:
(name: string | Function) => void使用
describe.todo可以为稍后实现的测试套件创建占位符。 报告中会显示这些套件的条目,方便你了解还需要实现多少测试。ts// 报告中将显示此套件的条目 describe.todo('unimplemented suite');
describe.each
类型:
(cases: ReadonlyArray<T>, ...args: any[]): (name: string | Function, fn: (...args: T[]) => void, options?: number | TestOptions) => void如果你有多个依赖于相同数据的测试,请使用
describe.each。tsdescribe.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}语法。
tsdescribe.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 代码块中使用,则作用于当前测试套件。当你将 Vitest 作为类型检查器运行时,不会调用这些钩子。
beforeEach
类型:
beforeEach(fn: () => Awaitable<void>, timeout?: number)注册一个回调函数,该函数会在当前作用域内的每个测试运行之前被调用。 如果该函数返回一个 Promise,Vitest 会等待该 Promise 解析完成后再运行测试。
你可以选择传递一个超时时间(以毫秒为单位),定义在终止之前等待的时间。默认值为 5 秒。
tsimport { beforeEach } from 'vitest'; beforeEach(async () => { // 在每次测试运行之前清除模拟数据并添加一些测试数据 await stopMocking(); await addUser({ name: 'John' }); });在这里,
beforeEach确保每个测试都有用户添加。从 Vitest v0.10.0 开始,
beforeEach也支持一个可选的清理函数(功能与afterEach类似)。tsimport { beforeEach } from 'vitest'; beforeEach(async () => { // 在每次测试运行之前调用 await prepareSomething(); // 清理函数,在每次测试运行之后调用一次 return async () => { await resetSomething(); }; });
afterEach
类型:
afterEach(fn: () => Awaitable<void>, timeout?: number)注册一个回调函数,该函数会在当前作用域内的每个测试完成后被调用。 如果该函数返回一个 Promise,Vitest 会等待该 Promise 解析完成后再继续。
你可以选择提供一个超时时间(以毫秒为单位),用于指定在终止之前等待的时间。默认值为 5 秒。
tsimport { afterEach } from 'vitest'; afterEach(async () => { await clearTestingData(); // 在每次测试运行后清除测试数据 });在这里,
afterEach确保在每次测试运行后清除测试数据。
beforeAll
类型:
beforeAll(fn: () => Awaitable<void>, timeout?: number)注册一个回调函数,该函数会在开始运行当前作用域内的所有测试之前被调用一次。 如果该函数返回一个 Promise,Vitest 会等待该 Promise 解析完成后再运行测试。
你可以选择提供一个超时时间(以毫秒为单位),用于指定在终止之前等待的时间。默认值为 5 秒。
tsimport { beforeAll } from 'vitest'; beforeAll(async () => { await startMocking(); // 在所有测试运行之前调用一次 });在这里,
beforeAll确保在测试运行之前设置模拟数据。从 Vitest v0.10.0 开始,
beforeAll也支持一个可选的清理函数(功能与afterAll类似)。tsimport { beforeAll } from 'vitest'; beforeAll(async () => { // 在所有测试运行之前调用一次 await startMocking(); // 清理函数,在所有测试运行之后调用一次 return async () => { await stopMocking(); }; });
afterAll
类型:
afterAll(fn: () => Awaitable<void>, timeout?: number)注册一个回调函数,该函数会在当前作用域内的所有测试运行完毕后被调用一次。 如果该函数返回一个 Promise,Vitest 会等待该 Promise 解析完成后再继续。
你可以选择提供一个超时时间(以毫秒为单位),用于指定在终止之前等待的时间。默认值为 5 秒。
tsimport { afterAll } from 'vitest'; afterAll(async () => { await stopMocking(); // 此方法在所有测试运行后被调用 });在这里,
afterAll确保在所有测试运行后调用stopMocking方法。