Skip to content

测试 ​

  • 别名: it
ts
function test(
  name: string | Function,
  body?: () => unknown,
  timeout?: number
): void
function test(
  name: string | Function,
  options: TestOptions,
  body?: () => unknown,
): void

test 或 it 定义了一组相关的期望。它接收测试名称和一个包含要测试的期望的函数。

可选地,你可以提供一个超时时间(毫秒)来指定等待多久后终止,或者提供一组 其他选项。默认超时时间为 5 秒,可以通过 testTimeout 全局配置。

ts
import { expect, test } from 'vitest'

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

WARNING

如果第一个参数是一个函数,其 name 属性将被用作测试名称。该函数本身不会被调用。

如果未提供测试主体,则测试被标记为 todo。

当测试函数返回一个 promise 时,运行器将等待其 resolved 以收集异步期望。如果 promise 被 rejected,测试将失败。

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 }, () => {
  // 一些目前会失败的逻辑
})

它们的工作方式完全相同。使用哪一种纯粹是风格选择。

timeout ​

  • 类型: number
  • 默认值: 5_000(由 testTimeout 配置)

测试超时时间(毫秒)。

WARNING

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

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 }, () => {
  // ...
})

retry ​

  • 默认值: 0(由 retry 配置)
  • 类型:
ts
type Retry = number | {
  /**
   * 如果测试失败,重试的次数。
   * @default 0
   */
  count?: number
  /**
   * 重试尝试之间的延迟(毫秒)。
   * @default 0
   */
  delay?: number
  /**
   * 根据错误确定是否应重试测试的条件。
   * - 如果是 RegExp,则针对错误消息进行测试
   * - 如果是函数,使用 TestError 对象调用;返回 true 以重试
   *
   * 注意:函数只能在测试文件中使用,不能在 vitest.config.ts 中使用,
   * 因为配置在传递给工作线程时会被序列化。
   *
   * @default undefined(对所有错误重试)
   */
  condition?: RegExp | ((error: TestError) => boolean)
}

测试的重试配置。如果是数字,指定重试次数。如果是对象,允许细粒度的重试控制。

注意,对象配置仅在 Vitest 4.1 及以上版本可用。

repeats ​

  • 类型: number
  • 默认值: 0

测试将再次运行的次数。如果设置为 0(默认值),测试将只运行一次。

这对于调试不稳定的测试很有用。

tags 4.1.0+ ​

  • 类型: string[]
  • 默认值: []

自定义用户 标签。如果标签未在 配置 中指定,测试将在开始前失败,除非手动禁用 strictTags。

ts
import { it } from 'vitest'

it('user returns data from db', { tags: ['db', 'flaky'] }, () => {
  // ...
})

meta 4.1.0+ ​

  • 类型: TaskMeta

附加可在报告器中使用的自定义 元数据。

WARNING

Vitest 会合并从套件或标签继承的顶层属性。但是,它不会对嵌套对象执行深度合并。

ts
import { describe, test } from 'vitest'

describe(
  'nested meta',
  {
    meta: {
      nested: { object: true, array: false },
    },
  },
  () => {
    test(
      'overrides part of meta',
      {
        meta: {
          nested: { object: false }
        },
      },
      ({ task }) => {
        // task.meta === { nested: { object: false } }
        // 注意数组丢失了,因为 "nested" 对象被覆盖了
      }
    )
  }
)

如果可能,建议使用非嵌套的 meta。

concurrent ​

此测试是否与套件中的其他并发测试并发运行。

将 concurrent 设置为 false 可退出从 describe.concurrent 或 sequence.concurrent 继承的并发执行:

ts
test('runs sequentially', { concurrent: false }, async () => {
  // ...
})

skip ​

  • 类型: boolean
  • 默认值: false
  • 别名: test.skip

是否应跳过测试。

only ​

  • 类型: boolean
  • 默认值: false
  • 别名: test.only

此测试是否应该是套件中唯一运行的测试。

todo ​

  • 类型: boolean
  • 默认值: false
  • 别名: test.todo

是否应跳过测试并标记为待办事项。

fails ​

测试是否预期会失败。如果失败了,测试将通过,否则将失败。

test.extend ​

  • 别名: it.extend

使用 test.extend 通过自定义夹具扩展测试上下文。这将返回一个新的 test,并且它也是可扩展的,因此你可以根据需要扩展它以组合更多夹具或覆盖现有夹具。请参阅 扩展测试上下文 以获取更多信息。

ts
import { test as baseTest, expect } from 'vitest'

export const test = baseTest
  // 简单值 - 类型推断为 { port: number; host: string }
  .extend('config', { port: 3000, host: 'localhost' })
  // 函数夹具 - 类型从返回值推断
  .extend('server', async ({ config }) => {
    // TypeScript 知道 config 是 { port: number; host: string }
    return `http://${config.host}:${config.port}`
  })

test('server uses correct port', ({ config, server }) => {
  // TypeScript 知道类型:
  // - config 是 { port: number; host: string }
  // - server 是 string
  expect(server).toBe('http://localhost:3000')
  expect(config.port).toBe(3000)
})

test.override 4.1.0+ ​

使用 test.override 可以为当前套件及其嵌套套件中的所有测试覆盖 fixture 值。此方法必须在 describe 块的顶层调用。更多信息请参阅覆盖 Fixture 值。

ts
import { test as baseTest, describe, expect } from 'vitest'

const test = baseTest
  .extend('dependency', 'default')
  .extend('dependant', ({ dependency }) => dependency)

describe('use scoped values', () => {
  test.override({ dependency: 'new' })

  test('uses scoped value', ({ dependant }) => {
    // `dependant` 使用新的覆盖值,该值作用于
    // 此套件中的所有测试
    expect(dependant).toEqual({ dependency: 'new' })
  })
})

test.scoped 3.1.0+ deprecated ​

  • 别名: it.scoped

已弃用

test.scoped 已弃用,推荐使用 test.override,并将在未来的主版本中移除。

test.override 的别名

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)
})

如果条件未知,你可以将其作为第一个参数提供给 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

在某些情况下,你可能会在不同的环境中多次运行测试,其中一些测试可能是特定于环境的。你可以使用 test.skipIf 来在条件为真时跳过测试,而不是用 if 包裹测试代码。

ts
import { assert, test } from 'vitest'

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

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

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', () => {
  // 此测试仅在开发环境中运行
})

test.only ​

  • 别名: it.only

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

ts
import { assert, test } from 'vitest'

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

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

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

shell
vitest interesting.test.ts

WARNING

Vitest 会检测测试是否在 CI 中运行,如果任何测试带有 only 标志,它将抛出错误。你可以通过 allowOnly 选项配置此行为。

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(/* ... */)

运行并发测试时,快照和断言必须使用本地 测试上下文 中的 expect,以确保检测到正确的测试。

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

请注意,如果测试是同步的,Vitest 仍将按顺序运行它们。

test.todo ​

  • 别名: it.todo

使用 test.todo 占位稍后实现的测试。报告中将显示该测试的条目,以便你知道还有多少测试需要实现。

ts
// 报告中将显示此测试的条目
test.todo('unimplemented test', () => {
  // 失败的实现...
})

TIP

如果测试没有主体,Vitest 会自动将测试标记为 todo。

test.fails ​

  • 别名: it.fails

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

ts
import { expect, test } from 'vitest'

test.fails('repro #1234', () => {
  expect(add(1, 2)).toBe(4)
})

此标志有助于跟踪库行为随时间的差异。例如,由于时间限制,你可以定义一个失败的测试而暂不修复问题。自 Vitest 4.1 起,标记为 fails 的测试会在测试摘要中跟踪。

test.each ​

  • 别名: it.each

TIP

虽然提供了 test.each 以兼容 Jest, 但 Vitest 还拥有 test.for,它具有集成 测试上下文 的额外功能。

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

  • %s: 字符串
  • %d: 数字
  • %i: 整数
  • %f: 浮点值
  • %j: json
  • %o: 对象
  • %#: 测试用例的基于 0 的索引
  • %$: 测试用例的基于 1 的索引
  • %%: 单个百分号 ('%')
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)
})

// this will return
// ✓ add(1, b) -> 1b
// ✓ add(2, b) -> 2b
// ✓ add(3, b) -> 3b
  • 第一行应为列名,用 | 分隔;
  • 后续一行或多行数据使用 ${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 使用其显示格式化器来格式化插值标题值。如果该值被截断得过短,你可以在配置文件中增加 taskTitleValueFormatTruncate。

test.for ​

  • 别名: it.for

替代 test.each 以提供 测试上下文。

与 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)
})

第二个参数是 测试上下文,可用于并发快照,例如:

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

test.describe 4.1.0+ ​

作用域 describe。参见 describe 获取更多信息。

test.suite 4.1.0+ ​

suite 的别名。参见 describe 获取更多信息。

test.beforeEach ​

作用域 beforeEach 钩子,继承自 test.extend 的类型。参见 beforeEach 获取更多信息。

test.afterEach ​

作用域 afterEach 钩子,继承自 test.extend 的类型。参见 afterEach 获取更多信息。

test.beforeAll ​

作用域 beforeAll 钩子,继承自 test.extend 的类型。参见 beforeAll 获取更多信息。

test.afterAll ​

作用域 afterAll 钩子,继承自 test.extend 的类型。参见 afterAll 获取更多信息。

test.aroundEach 4.1.0+ ​

作用域 aroundEach 钩子,继承自 test.extend 的类型。参见 aroundEach 获取更多信息。

test.aroundAll 4.1.0+ ​

作用域 aroundAll 钩子,继承自 test.extend 的类型。参见 aroundAll 获取更多信息。

bench experimental ​

Vitest 5 中已更新

基准测试 API 已被重写。bench 不再是从 vitest 顶层导入的内容,bench.skip / bench.only / bench.todo 辅助方法已被移除。bench 现在是一个从 test() 内部访问的 测试上下文字段。

有关新 API,请参见 基准测试指南。