测试 API 参考
以下类型用于下面的类型签名
type Awaitable<T> = T | PromiseLike<T>
type TestFunction = () => Awaitable<void>
interface TestOptions {
/**
* Will fail the test if it takes too long to execute
*/
timeout?: number
/**
* Will retry the test specific number of times if it fails
*
* @default 0
*/
retry?: number
/**
* Will repeat the same test several times even if it fails each time
* If you have "retry" option and it fails, it will use every retry in each cycle
* Useful for debugging random failings
*
* @default 0
*/
repeats?: number
}
Vitest 1.3.0 不再推荐使用选项作为最后一个参数。您将看到弃用消息,直到 2.0.0 版本,届时此语法将被删除。如果您需要传递选项,请使用 test
函数的第二个参数
import { test } from 'vitest'
test('flaky test', () => {}, { retry: 3 })
test('flaky test', { retry: 3 }, () => {})
当测试函数返回一个 Promise 时,运行器将等待它被解析以收集异步期望。如果 Promise 被拒绝,测试将失败。
提示
在 Jest 中,TestFunction
也可以是 (done: DoneCallback) => void
类型。如果使用此形式,测试将不会在调用 done
之前结束。您可以使用 async
函数实现相同的效果,请参阅 迁移指南 Done Callback 部分。
从 Vitest 1.3.0 开始,大多数选项都支持点语法和对象语法,您可以根据自己的喜好使用任何一种风格。
import { } from 'vitest'
.('skipped test', () => {
// some logic that fails right now
})
import { } from 'vitest'
('skipped test', { : true }, () => {
// some logic that fails right now
})
test
- 别名:
it
test
定义了一组相关的期望。它接收测试名称和一个包含要测试的期望的函数。
可选地,您可以提供一个超时时间(以毫秒为单位),用于指定在终止之前等待多长时间。默认值为 5 秒,可以通过 testTimeout 在全局范围内配置
import { , } from 'vitest'
('should work as expected', () => {
(.(4)).(2)
})
test.extend 0.32.3+
- 别名:
it.extend
使用 test.extend
用自定义夹具扩展测试上下文。这将返回一个新的 test
,它也是可扩展的,因此您可以根据需要通过扩展它来组合更多夹具或覆盖现有夹具。有关更多信息,请参阅 扩展测试上下文。
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
来避免运行它们。
import { , } from 'vitest'
.('skipped test', () => {
// Test skipped, no error
.(.(4), 3)
})
您也可以通过在它的 上下文 上动态调用 skip
来跳过测试
import { , } from 'vitest'
('skipped test', () => {
.()
// Test skipped, no error
.(.(4), 3)
})
test.skipIf
- 别名:
it.skipIf
在某些情况下,您可能使用不同的环境多次运行测试,而某些测试可能是特定于环境的。与其用 if
包裹测试代码,不如使用 test.skipIf
,当条件为真时跳过测试。
import { , } from 'vitest'
const = .. === 'development'
.()('prod only test', () => {
// this test only runs in production
})
警告
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
test.runIf
- 别名:
it.runIf
与 test.skipIf 相反。
import { , } from 'vitest'
const = .. === 'development'
.()('dev only test', () => {
// this test only runs in development
})
警告
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
test.only
- 别名:
it.only
使用 test.only
仅在给定套件中运行某些测试。这在调试时很有用。
可选地,您可以提供一个超时时间(以毫秒为单位),用于指定在终止之前等待多长时间。默认值为 5 秒,可以通过 testTimeout 在全局范围内配置。
import { , } from 'vitest'
.('test', () => {
// Only this test (and others marked with only) are run
.(.(4), 2)
})
有时在特定文件中运行 only
测试非常有用,忽略整个测试套件中的所有其他测试,这些测试会污染输出。
为了做到这一点,请使用包含要测试的测试的特定文件运行 vitest
。
# vitest interesting.test.ts
test.concurrent
- 别名:
it.concurrent
test.concurrent
将连续测试标记为并行运行。它接收测试名称、一个包含要收集的测试的异步函数,以及一个可选的超时时间(以毫秒为单位)。
import { , } from 'vitest'
// The two tests marked with concurrent will be run in parallel
('suite', () => {
('serial test', async () => { /* ... */ })
.('concurrent test 1', async () => { /* ... */ })
.('concurrent test 2', async () => { /* ... */ })
})
test.skip
、test.only
和 test.todo
可与并发测试一起使用。以下所有组合都是有效的
test.concurrent(/* ... */)
test.skip.concurrent(/* ... */) // or test.concurrent.skip(/* ... */)
test.only.concurrent(/* ... */) // or test.concurrent.only(/* ... */)
test.todo.concurrent(/* ... */) // or test.concurrent.todo(/* ... */)
在运行并发测试时,快照和断言必须使用来自本地 测试上下文 的 expect
来确保检测到正确的测试。
test.concurrent('test 1', async ({ expect }) => {
expect(foo).toMatchSnapshot()
})
test.concurrent('test 2', async ({ expect }) => {
expect(foo).toMatchSnapshot()
})
警告
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
test.sequential
- 别名:
it.sequential
test.sequential
将测试标记为顺序测试。如果您想在 describe.concurrent
中或使用 --sequence.concurrent
命令选项按顺序运行测试,这将很有用。
// with config option { sequence: { concurrent: true } }
('concurrent test 1', async () => { /* ... */ })
('concurrent test 2', async () => { /* ... */ })
.('sequential test 1', async () => { /* ... */ })
.('sequential test 2', async () => { /* ... */ })
// within concurrent suite
.('suite', () => {
('concurrent test 1', async () => { /* ... */ })
('concurrent test 2', async () => { /* ... */ })
.('sequential test 1', async () => { /* ... */ })
.('sequential test 2', async () => { /* ... */ })
})
test.todo
- 别名:
it.todo
使用 test.todo
来存根以后要实现的测试。报告中将显示一个条目,用于指示您还需要实现多少个测试。
// An entry will be shown in the report for this test
test.todo('unimplemented test')
test.fails
- 别名:
it.fails
使用 test.fails
来指示断言将显式失败。
import { , } from 'vitest'
function () {
return new ( => (1))
}
.('fail test', async () => {
await (())..(1)
})
警告
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
test.each
- 别名:
it.each
当您需要使用不同的变量运行相同的测试时,使用 test.each
。您可以使用 printf 格式化 在测试名称中按测试函数参数的顺序注入参数。
%s
:字符串%d
:数字%i
:整数%f
:浮点值%j
:json%o
:对象%#
:测试用例的索引%%
:单个百分号('%')
.([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
])('add(%i, %i) -> %i', (, , ) => {
( + ).()
})
// this will return
// ✓ add(1, 1) -> 2
// ✓ add(1, 2) -> 3
// ✓ add(2, 1) -> 3
如果您使用对象作为参数,也可以使用 $
前缀访问对象属性
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)
})
// this will return
// ✓ add(1, 1) -> 2
// ✓ add(1, 2) -> 3
// ✓ add(2, 1) -> 3
如果您使用对象作为参数,也可以使用 .
访问对象属性
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)
})
// this will return
// ✓ add(1, b) -> 1b
// ✓ add(2, b) -> 2b
// ✓ add(3, b) -> 3b
从 Vitest 0.25.3 开始,您也可以使用模板字符串表格。
- 第一行应该是列名,用
|
分隔; - 使用
${value}
语法作为模板字面量表达式提供的多个后续数据行。
.`
a | b | expected
${1} | ${1} | ${2}
${'a'} | ${'b'} | ${'ab'}
${[]} | ${'b'} | ${'b'}
${{}} | ${'b'} | ${'[object Object]b'}
${{ : 1 }} | ${'b'} | ${'[object Object]b'}
`('returns $expected when $a is added $b', ({ , , }) => {
( + ).()
})
如果您想访问 TestContext
,请使用带有单个测试的 describe.each
。
提示
Vitest 使用 Chai format
方法处理 $values
。如果值被截断太多,您可以在配置文件中增加 chaiConfig.truncateThreshold。
警告
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
bench
- 类型:
(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void
bench
定义了一个基准测试。在 Vitest 术语中,基准测试是一个定义一系列操作的函数。Vitest 多次运行此函数以显示不同的性能结果。
Vitest 在幕后使用 tinybench
库,继承了所有可作为第三个参数使用的选项。
import { } from 'vitest'
('normal sorting', () => {
const = [1, 5, 4, 2, 3]
.((, ) => {
return -
})
}, { : 1000 })
export interface Options {
/**
* time needed for running a benchmark task (milliseconds)
* @default 500
*/
time?: number
/**
* number of times that a task should run if even the time option is finished
* @default 10
*/
iterations?: number
/**
* function to get the current timestamp in milliseconds
*/
now?: () => number
/**
* An AbortSignal for aborting the benchmark
*/
signal?: AbortSignal
/**
* warmup time (milliseconds)
* @default 100ms
*/
warmupTime?: number
/**
* warmup iterations
* @default 5
*/
warmupIterations?: number
/**
* setup function to run before each benchmark task (cycle)
*/
setup?: Hook
/**
* teardown function to run after each benchmark task (cycle)
*/
teardown?: Hook
}
bench.skip
- 类型:
(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void
您可以使用 bench.skip
语法跳过运行某些基准测试。
import { } from 'vitest'
.('normal sorting', () => {
const = [1, 5, 4, 2, 3]
.((, ) => {
return -
})
})
bench.only
- 类型:
(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void
使用 bench.only
仅在给定套件中运行某些基准测试。这在调试时很有用。
import { } from 'vitest'
.('normal sorting', () => {
const = [1, 5, 4, 2, 3]
.((, ) => {
return -
})
})
bench.todo
- 类型:
(name: string | Function) => void
使用 bench.todo
来存根以后要实现的基准测试。
import { } from 'vitest'
.('unimplemented test')
describe
当您在文件的顶层使用 test
或 bench
时,它们将作为其隐式套件的一部分被收集。使用 describe
,您可以在当前上下文中定义一个新的套件,作为一组相关的测试或基准测试以及其他嵌套套件。套件允许您组织测试和基准测试,以便报告更清晰。
// basic.spec.ts
// organizing tests
import { , , } from 'vitest'
const = {
: true,
: 32,
}
('person', () => {
('person is defined', () => {
().()
})
('is active', () => {
(.).()
})
('age limit', () => {
(.).(32)
})
})
// basic.bench.ts
// organizing benchmarks
import { , } from 'vitest'
('sort', () => {
('normal', () => {
const = [1, 5, 4, 2, 3]
.((, ) => {
return -
})
})
('reverse', () => {
const = [1, 5, 4, 2, 3]
.().((, ) => {
return -
})
})
})
如果您有测试或基准测试的层次结构,您也可以嵌套 describe 块
import { , , } from 'vitest'
function (: number | string) {
if (typeof !== 'number')
throw new ('Value must be a number')
return .(2).().(/\B(?=(\d{3})+(?!\d))/g, ',')
}
('numberToCurrency', () => {
('given an invalid number', () => {
('composed of non-numbers to throw error', () => {
(() => ('abc')).()
})
})
('given a valid number', () => {
('returns the correct currency format', () => {
((10000)).('10,000.00')
})
})
})
describe.skip
- 别名:
suite.skip
在套件中使用 describe.skip
来避免运行特定的 describe 块。
import { , , } from 'vitest'
.('skipped suite', () => {
('sqrt', () => {
// Suite skipped, no error
.(.(4), 3)
})
})
describe.skipIf
- 别名:
suite.skipIf
在某些情况下,您可能使用不同的环境多次运行套件,而某些套件可能是特定于环境的。与其用 if
包裹套件,不如使用 describe.skipIf
,当条件为真时跳过套件。
import { , } from 'vitest'
const = .. === 'development'
.()('prod only test suite', () => {
// this test suite only runs in production
})
警告
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
describe.runIf
- 别名:
suite.runIf
与 describe.skipIf 相反。
import { , , } from 'vitest'
const = .. === 'development'
.()('dev only test suite', () => {
// this test suite only runs in development
})
警告
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
describe.only
- 类型:
(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void
使用 describe.only
仅运行某些套件
// Only this suite (and others marked with only) are run
.('suite', () => {
('sqrt', () => {
.(.(4), 3)
})
})
('other suite', () => {
// ... will be skipped
})
有时在特定文件中运行 only
测试非常有用,忽略整个测试套件中的所有其他测试,这些测试会污染输出。
为了做到这一点,请使用包含要测试的测试的特定文件运行 vitest
。
# vitest interesting.test.ts
describe.concurrent
- 别名:
suite.concurrent
describe.concurrent
在套件中将每个测试标记为并发测试
// All tests within this suite will be run in parallel
.('suite', () => {
('concurrent test 1', async () => { /* ... */ })
('concurrent test 2', async () => { /* ... */ })
.('concurrent test 3', async () => { /* ... */ })
})
.skip
、.only
和 .todo
可与并发套件一起使用。以下所有组合都是有效的
describe.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()
})
})
警告
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
describe.sequential
- 别名:
suite.sequential
describe.sequential
在套件中标记每个测试为顺序执行。如果您希望在 describe.concurrent
中或使用 --sequence.concurrent
命令选项在套件内按顺序运行测试,这将很有用。
.('suite', () => {
('concurrent test 1', async () => { /* ... */ })
('concurrent test 2', async () => { /* ... */ })
.('', () => {
('sequential test 1', async () => { /* ... */ })
('sequential test 2', async () => { /* ... */ })
})
})
describe.shuffle
- 别名:
suite.shuffle
Vitest 提供了一种通过 CLI 标志 --sequence.shuffle
或配置选项 sequence.shuffle
以随机顺序运行所有测试的方法,但如果您希望仅让测试套件的一部分以随机顺序运行测试,可以使用此标志标记它。
.('suite', () => {
('random test 1', async () => { /* ... */ })
('random test 2', async () => { /* ... */ })
('random test 3', async () => { /* ... */ })
})
// order depends on sequence.seed option in config (Date.now() by default)
.skip
、.only
和 .todo
可与随机套件一起使用。
警告
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
describe.todo
- 别名:
suite.todo
使用 describe.todo
来存根稍后要实现的套件。报告中将显示一个条目,以便您知道还需要实现多少测试。
// An entry will be shown in the report for this suite
describe.todo('unimplemented suite')
describe.each
- 别名:
suite.each
如果您有多个测试依赖于相同的数据,请使用 describe.each
。
.([
{ : 1, : 1, : 2 },
{ : 1, : 2, : 3 },
{ : 2, : 1, : 3 },
])('describe object add($a, $b)', ({ , , }) => {
(`returns ${}`, () => {
( + ).()
})
(`returned value not be greater than ${}`, () => {
( + )..()
})
(`returned value not be less than ${}`, () => {
( + )..()
})
})
从 Vitest 0.25.3 开始,您也可以使用模板字符串表格。
- 第一行应该是列名,用
|
分隔; - 使用
${value}
语法作为模板字面量表达式提供的多个后续数据行。
.`
a | b | expected
${1} | ${1} | ${2}
${'a'} | ${'b'} | ${'ab'}
${[]} | ${'b'} | ${'b'}
${{}} | ${'b'} | ${'[object Object]b'}
${{ : 1 }} | ${'b'} | ${'[object Object]b'}
`('describe template string add($a, $b)', ({ , , }) => {
(`returns ${}`, () => {
( + ).()
})
})
警告
当使用 Vitest 作为 类型检查器 时,您不能使用此语法。
设置和拆卸
这些函数允许您挂钩到测试的生命周期,以避免重复设置和拆卸代码。它们适用于当前上下文:如果它们在顶层使用,则为文件,如果它们在 describe
块内,则为当前套件。当您将 Vitest 作为类型检查器运行时,这些钩子不会被调用。
beforeEach
- 类型:
beforeEach(fn: () => Awaitable<void>, timeout?: number)
注册一个回调函数,该函数将在当前上下文中运行的每个测试之前被调用。如果函数返回一个 Promise,Vitest 将等待 Promise 解决后才运行测试。
可以选择传递一个超时时间(以毫秒为单位),定义在终止之前等待多长时间。默认值为 5 秒。
import { beforeEach } from 'vitest'
beforeEach(async () => {
// Clear mocks and add some testing data after before each test run
await stopMocking()
await addUser({ name: 'John' })
})
在这里,beforeEach
确保为每个测试添加用户。
从 Vitest v0.10.0 开始,beforeEach
还接受一个可选的清理函数(等效于 afterEach
)。
import { beforeEach } from 'vitest'
beforeEach(async () => {
// called once before each test run
await prepareSomething()
// clean up function, called once after each test run
return async () => {
await resetSomething()
}
})
afterEach
- 类型:
afterEach(fn: () => Awaitable<void>, timeout?: number)
注册一个回调函数,该函数将在当前上下文中完成的每个测试之后被调用。如果函数返回一个 Promise,Vitest 将等待 Promise 解决后才继续。
可以选择提供一个超时时间(以毫秒为单位),用于指定在终止之前等待多长时间。默认值为 5 秒。
import { afterEach } from 'vitest'
afterEach(async () => {
await clearTestingData() // clear testing data after each test run
})
在这里,afterEach
确保在每个测试运行后清除测试数据。
提示
Vitest 1.3.0 添加了 onTestFinished
钩子。您可以在测试执行期间调用它,以便在测试完成运行后清理任何状态。
beforeAll
- 类型:
beforeAll(fn: () => Awaitable<void>, timeout?: number)
注册一个回调函数,该函数将在开始运行当前上下文中的所有测试之前被调用一次。如果函数返回一个 Promise,Vitest 将等待 Promise 解决后才运行测试。
可以选择提供一个超时时间(以毫秒为单位),用于指定在终止之前等待多长时间。默认值为 5 秒。
import { beforeAll } from 'vitest'
beforeAll(async () => {
await startMocking() // called once before all tests run
})
在这里,beforeAll
确保在测试运行之前设置模拟数据。
从 Vitest v0.10.0 开始,beforeAll
还接受一个可选的清理函数(等效于 afterAll
)。
import { beforeAll } from 'vitest'
beforeAll(async () => {
// called once before all tests run
await startMocking()
// clean up function, called once after all tests run
return async () => {
await stopMocking()
}
})
afterAll
- 类型:
afterAll(fn: () => Awaitable<void>, timeout?: number)
注册一个回调函数,该函数将在当前上下文中的所有测试运行完毕后被调用一次。如果函数返回一个 Promise,Vitest 将等待 Promise 解决后才继续。
可以选择提供一个超时时间(以毫秒为单位),用于指定在终止之前等待多长时间。默认值为 5 秒。
import { afterAll } from 'vitest'
afterAll(async () => {
await stopMocking() // this method is called after all tests run
})
在这里,afterAll
确保在所有测试运行完毕后调用 stopMocking
方法。
测试钩子
Vitest 提供了一些钩子,您可以在测试执行期间调用它们,以便在测试完成运行后清理状态。
警告
如果在测试主体之外调用这些钩子,它们将抛出错误。
onTestFinished 1.3.0+
此钩子始终在测试完成运行后被调用。它在 afterEach
钩子之后被调用,因为它们可能会影响测试结果。它接收一个包含当前测试结果的 TaskResult
对象。
import { onTestFinished, test } from 'vitest'
test('performs a query', () => {
const db = connectDb()
onTestFinished(() => db.close())
db.query('SELECT * FROM users')
})
警告
如果您正在并发运行测试,您应该始终从测试上下文中使用 onTestFinished
钩子,因为 Vitest 不会在全局钩子中跟踪并发测试
import { test } from 'vitest'
test.concurrent('performs a query', ({ onTestFinished }) => {
const db = connectDb()
onTestFinished(() => db.close())
db.query('SELECT * FROM users')
})
此钩子在创建可重用逻辑时特别有用
// this can be in a separate file
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([])
})
提示
此钩子始终以相反的顺序被调用,并且不受 sequence.hooks
选项的影响。
onTestFailed
此钩子仅在测试失败后被调用。它在 afterEach
钩子之后被调用,因为它们可能会影响测试结果。它接收一个包含当前测试结果的 TaskResult
对象。此钩子对于调试很有用。
import { onTestFailed, test } from 'vitest'
test('performs a query', () => {
const db = connectDb()
onTestFailed((e) => {
console.log(e.result.errors)
})
db.query('SELECT * FROM users')
})
警告
如果您正在并发运行测试,您应该始终从测试上下文中使用 onTestFailed
钩子,因为 Vitest 不会在全局钩子中跟踪并发测试
import { test } from 'vitest'
test.concurrent('performs a query', ({ onTestFailed }) => {
const db = connectDb()
onTestFailed((result) => {
console.log(result.errors)
})
db.query('SELECT * FROM users')
})