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

与其他测试运行器的比较

页面导航

测试上下文 ​

受 Playwright Fixtures 的启发,Vitest 的测试上下文允许您定义可在测试中使用的工具、状态和夹具(fixture)。

用法 ​

每个测试回调函数的第一个参数是测试上下文。

ts
import { it } from 'vitest';

it('should work', ({ task }) => {
  // 打印测试名称
  console.log(task.name);
});

内置测试上下文 ​

task ​

一个包含测试元数据的只读对象。

expect ​

与当前测试绑定的 expect API:

ts
import { it } from 'vitest';

it('math is easy', ({ expect }) => {
  expect(2 + 2).toBe(4);
});

此 API 对于并发运行快照测试非常有用,因为全局的 expect 无法正确跟踪这些测试:

ts
import { it } from 'vitest';

it.concurrent('math is easy', ({ expect }) => {
  expect(2 + 2).toMatchInlineSnapshot();
});

it.concurrent('math is hard', ({ expect }) => {
  expect(2 * 2).toMatchInlineSnapshot();
});

skip ​

ts
function skip(note?: string): never;
function skip(condition: boolean, note?: string): void;

跳过后续测试的执行,并将测试标记为已跳过:

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

it('math is hard', ({ skip }) => {
  skip();
  expect(2 + 2).toBe(5);
});

从 Vitest 3.1 开始,您可以传入一个布尔参数以有条件地跳过测试:

ts
it('math is hard', ({ skip, mind }) => {
  skip(mind === 'foggy');
  expect(2 + 2).toBe(5);
});

annotate 3.2.0+ ​

ts
function annotate(
  message: string,
  attachment?: TestAttachment
): Promise<TestAnnotation>;

function annotate(
  message: string,
  type?: string,
  attachment?: TestAttachment
): Promise<TestAnnotation>;

添加一个将由您的 reporter 显示的测试注解。

ts
test('annotations API', async ({ annotate }) => {
  await annotate('https://github.com/vitest-dev/vitest/pull/7953', 'issues');
});

signal 3.2.0+ ​

一个 AbortSignal,可由 Vitest 中止。信号会在以下情况下被中止:

  • 测试超时
  • 用户使用 Ctrl+C 手动取消了测试运行
  • vitest.cancelCurrentRun 被以编程方式调用
  • 另一个并行测试失败且设置了 bail 标志
ts
it('stop request when test times out', async ({ signal }) => {
  await fetch('/resource', { signal });
}, 2000);

onTestFailed ​

当前测试绑定的 onTestFailed 钩子。当您并发运行测试时,如果只需要对特定测试进行特殊处理,这个 API 将非常有用。

onTestFinished ​

当前测试绑定的 onTestFinished 钩子。当您并发运行测试时,如果只需要对特定测试进行特殊处理,这个 API 将非常有用。

扩展测试上下文 ​

Vitest 提供了两种扩展测试上下文的方式。

test.extend ​

与 Playwright 类似,您可以使用此方法定义自己的 test API,其中包含自定义夹具,并可在任何地方重用。

例如,我们首先创建一个包含两个夹具(todos 和 archive)的 test 收集器。

ts
import { test as baseTest } from 'vitest';

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

export const test = baseTest.extend({
  todos: async ({}, use) => {
    // 在每个测试函数之前设置夹具
    todos.push(1, 2, 3);

    // 使用夹具值
    await use(todos);

    // 在每个测试函数之后清理夹具
    todos.length = 0;
  },
  archive,
});

接下来,我们可以导入并使用它。

ts
import { expect } from 'vitest';
import { test } from './my-test.js';

test('add items to todos', ({ todos }) => {
  expect(todos.length).toBe(3);

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

test('move items from todos to archive', ({ todos, archive }) => {
  expect(todos.length).toBe(3);
  expect(archive.length).toBe(0);

  archive.push(todos.pop());
  expect(todos.length).toBe(2);
  expect(archive.length).toBe(1);
});

我们还可以通过扩展 test 来添加更多夹具或覆盖现有夹具。

ts
import { test as todosTest } from './my-test.js';

export const test = todosTest.extend({
  settings: {
    // ...
  },
});

夹具初始化 ​

Vitest 运行器将根据使用情况智能地初始化您的夹具,并将其注入到测试上下文中。

ts
import { test as baseTest } from 'vitest';

const test = baseTest.extend<{
  todos: number[];
  archive: number[];
}>({
  todos: async ({ task }, use) => {
    await use([1, 2, 3]);
  },
  archive: [],
});

// todos 将不会运行
test('skip', () => {});
test('skip', ({ archive }) => {});

// todos 将运行
test('run', ({ todos }) => {});

WARNING

当使用 test.extend() 配合夹具时,您应始终使用对象解构模式 { todos } 来访问夹具函数和测试函数中的上下文。

ts
test('context must be destructured', (context) => { 
  expect(context.todos.length).toBe(2)
})

test('context must be destructured', ({ todos }) => { 
  expect(todos.length).toBe(2)
})

自动夹具 ​

Vitest 还支持夹具的元组语法,允许您为每个夹具传递选项。例如,您可以使用它来显式初始化夹具,即使该夹具未在测试中使用。

ts
import { test as base } from 'vitest';

const test = base.extend({
  fixture: [
    async ({}, use) => {
      // 此函数将运行
      setup();
      await use();
      teardown();
    },
    { auto: true }, // 标记为自动夹具
  ],
});

test('works correctly');

默认夹具 ​

自 Vitest 3 起,您可以在不同的项目中提供不同的值。要启用此功能,请将 { injected: true } 传递给选项。如果项目配置中未指定该键,则将使用默认值。

ts
import { test as base } from 'vitest';

const test = base.extend({
  url: [
    // 如果配置中未定义 "url",则使用默认值
    '/default',
    // 将夹具标记为 "injected" 以允许覆盖
    { injected: true },
  ],
});

test('works correctly', ({ url }) => {
  // url 在 "project-new" 中是 "/default"
  // url 在 "project-full" 中是 "/full"
  // url 在 "project-empty" 中是 "/empty"
});
ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    projects: [
      {
        test: {
          name: 'project-new',
        },
      },
      {
        test: {
          name: 'project-full',
          provide: {
            url: '/full',
          },
        },
      },
      {
        test: {
          name: 'project-empty',
          provide: {
            url: '/empty',
          },
        },
      },
    ],
  },
});

将值作用域到套件 3.1.0+ ​

从 Vitest 3.1 开始,您可以使用 test.scoped API 覆盖每个套件及其子套件的上下文值:

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

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

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

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

  describe('keeps using scoped value', () => {
    test('uses scoped value', ({ dependant }) => {
      // 嵌套套件继承了该值
      expect(dependant).toEqual({ dependency: 'new' });
    });
  });
});

test('keep using the default values', ({ dependant }) => {
  // `dependency` 正在使用默认值
  // 在使用 .scoped 的套件之外
  expect(dependant).toEqual({ dependency: 'default' });
});

如果您有一个上下文值依赖于动态变量(例如数据库连接),此 API 将特别有用:

ts
const test = baseTest.extend<{
  db: Database;
  schema: string;
}>({
  db: async ({ schema }, use) => {
    const db = await createDb({ schema });
    await use(db);
    await cleanup(db);
  },
  schema: '',
});

describe('one type of schema', () => {
  test.scoped({ schema: 'schema-1' });

  // ... tests
});

describe('another type of schema', () => {
  test.scoped({ schema: 'schema-2' });

  // ... tests
});

每作用域上下文 3.2.0+ ​

您可以定义按文件或按工作线程初始化一次的上下文。它以与常规夹具相同的方式初始化,并带有一个对象参数:

ts
import { test as baseTest } from 'vitest';

export const test = baseTest.extend({
  perFile: [({}, { use }) => use([]), { scope: 'file' }],
  perWorker: [({}, { use }) => use([]), { scope: 'worker' }],
});

除非夹具选项中包含 auto: true,否则该值会在任何测试首次访问它时初始化——在这种情况下,该值会在任何测试运行之前初始化。

ts
const test = baseTest.extend({
  perFile: [
    ({}, { use }) => use([]),
    {
      scope: 'file',
      // 始终在任何测试之前运行此钩子
      auto: true,
    },
  ],
});

worker 作用域将为每个工作线程运行一次夹具。运行中的工作线程数量取决于各种因素。默认情况下,每个文件都在单独的工作线程中运行,因此 file 和 worker 作用域的工作方式相同。

但是,如果您禁用隔离,则工作线程数量将受 maxWorkers 或 poolOptions 配置的限制。

请注意,在 vmThreads 或 vmForks 中运行测试时,指定 scope: 'worker' 的工作方式与 scope: 'file' 相同。存在此限制的原因是每个测试文件都有自己的 VM 上下文,因此如果 Vitest 只初始化一次,一个上下文可能会泄漏到另一个上下文,并导致许多引用不一致(例如,同一类的实例将引用不同的构造函数)。

TypeScript ​

要为所有自定义上下文提供夹具类型,您可以将夹具类型作为泛型传递。

ts
interface MyFixtures {
  todos: number[];
  archive: number[];
}

const test = baseTest.extend<MyFixtures>({
  todos: [],
  archive: [],
});

test('types are defined correctly', ({ todos, archive }) => {
  expectTypeOf(todos).toEqualTypeOf<number[]>();
  expectTypeOf(archive).toEqualTypeOf<number[]>();
});

类型推断

请注意,Vitest 不支持在调用 use 函数时推断类型。在调用 test.extend 时,最好始终将整个上下文类型作为泛型类型传递:

ts
import { test as baseTest } from 'vitest';

const test = baseTest.extend<{
  todos: number[];
  schema: string;
}>({
  todos: ({ schema }, use) => use([]),
  schema: 'test',
});

test('types are correct', ({
  todos, // number[]
  schema, // string
}) => {
  // ...
});

beforeEach 和 afterEach ​

已弃用

这是一种过时的上下文扩展方式,当 test 使用 test.extend 扩展时,它将不起作用。

每个测试的上下文都是不同的。您可以在 beforeEach 和 afterEach 钩子中访问并扩展它们。

ts
import { beforeEach, it } from 'vitest';

beforeEach(async context => {
  // 扩展上下文
  context.foo = 'bar';
});

it('should work', ({ foo }) => {
  console.log(foo); // 'bar'
});

TypeScript ​

要为所有自定义上下文提供属性类型,您可以通过添加以下内容来增强 TestContext 类型:

ts
declare module 'vitest' {
  export interface TestContext {
    foo?: string;
  }
}

如果您只想为特定的 beforeEach、afterEach、it 和 test 钩子提供属性类型,您可以将类型作为泛型传递。

ts
interface LocalTestContext {
  foo: string;
}

beforeEach<LocalTestContext>(async context => {
  // context 的类型是 'TestContext & LocalTestContext'
  context.foo = 'bar';
});

it<LocalTestContext>('should work', ({ foo }) => {
  // foo 的类型是 'string'
  console.log(foo); // 'bar'
});
Pager
上一页源内测试
下一页测试注解

基于 MIT 许可证 发布。

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

https://vitest.dev/guide/test-context

基于 MIT 许可证 发布。

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