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

与其他测试运行器的比较

页面导航

迁移指南 ​

迁移到 Vitest 3.0 ​

测试选项作为第三个参数 ​

Vitest 3.0 在将对象作为 test 或 describe 函数的第三个参数时会打印警告:

ts
test('validation works', () => {
  // ...
}, { retry: 3 }); 

test('validation works', { retry: 3 }, () => { 
  // ...
});

在下一个主要版本中,如果第三个参数是对象,将会抛出错误。请注意,超时时间(数字)未被弃用:

ts
test('validation works', () => {
  // ...
}, 1000); // Ok ✅

browser.name 和 browser.providerOptions 已弃用 ​

在 Vitest 4 中,browser.name 和 browser.providerOptions 都将被移除。请使用新的 browser.instances 选项:

ts
export default defineConfig({
  test: {
    browser: {
      name: 'chromium', 
      providerOptions: { 
        launch: { devtools: true }, 
      }, 
      instances: [ 
        { 
          browser: 'chromium', 
          launch: { devtools: true }, 
        }, 
      ], 
    },
  },
})

通过新的 browser.instances 字段,您还可以指定多个浏览器配置。

spy.mockReset 现在恢复原始实现 ​

以前,在不重新应用 spy 的情况下,没有很好的方法能将 spy 重置为原始实现。现在,spy.mockReset 会将实现函数重置为原始实现,而不是一个假的空操作。

ts
const foo = {
  bar: () => 'Hello, world!',
};

vi.spyOn(foo, 'bar').mockImplementation(() => 'Hello, mock!');

foo.bar(); // 'Hello, mock!'

foo.bar.mockReset();

foo.bar(); // undefined
foo.bar(); // 'Hello, world!'

vi.spyOn 如果方法已被模拟,则重用模拟 ​

以前,Vitest 在监视一个对象时总是会分配一个新的 spy。这导致 mockRestore 出现错误,因为它会将 spy 恢复到之前的 spy,而不是原始函数:

ts
vi.spyOn(fooService, 'foo').mockImplementation(() => 'bar');
vi.spyOn(fooService, 'foo').mockImplementation(() => 'bar');
vi.restoreAllMocks();
vi.isMockFunction(fooService.foo); // true
vi.isMockFunction(fooService.foo); // false

伪造计时器默认值 ​

Vitest 不再为 fakeTimers.toFake 提供默认选项。现在,Vitest 将模拟任何可用的计时器相关 API(nextTick 除外)。具体来说,当调用 vi.useFakeTimers 时,performance.now() 现在会被模拟。

ts
vi.useFakeTimers();

performance.now(); // original
performance.now(); // fake

您可以通过在调用 vi.useFakeTimers 时或在全局配置中指定计时器来恢复之前的行为:

ts
export default defineConfig({
  test: {
    fakeTimers: {
      toFake: [ 
        'setTimeout', 
        'clearTimeout', 
        'setInterval', 
        'clearInterval', 
        'setImmediate', 
        'clearImmediate', 
        'Date', 
      ] 
    },
  },
})

更严格的错误相等性检查 ​

Vitest 现在在通过 toEqual 或 toThrowError 比较错误时检查更多属性。Vitest 现在会比较 name、message、cause 和 AggregateError.errors。对于 Error.cause,比较是非对称进行的:

ts
expect(new Error('hi', { cause: 'x' })).toEqual(new Error('hi')); // ✅
expect(new Error('hi')).toEqual(new Error('hi', { cause: 'x' })); // ❌

除了检查更多属性外,Vitest 现在还会比较错误原型。例如,如果抛出 TypeError,相等性检查应该引用 TypeError,而不是 Error:

ts
expect(() => {
  throw new TypeError('type error');
})
  .toThrowError(new Error('type error')) 
  .toThrowError(new TypeError('type error')); 

有关更多详细信息,请参阅 PR:#5876。

Vite 6 中默认不解析 module 条件导出 ​

Vite 6 允许更灵活的 resolve.conditions 选项,而 Vitest 默认配置为排除 module 条件导出。 关于 Vite 方面的更改详情,请参阅 Vite 6 迁移指南。

Custom 类型已弃用 API ​

Custom 类型现在是 Test 类型的别名。请注意,Vitest 在 2.1 中更新了公共类型,并将导出的名称更改为 RunnerCustomCase 和 RunnerTestCase:

ts
import {
  RunnerCustomCase, 
  RunnerTestCase, 
} from 'vitest';

如果您正在使用 getCurrentSuite().custom(),返回任务的 type 现在等于 'test'。Custom 类型将在 Vitest 4 中移除。

WorkspaceSpec 类型不再使用 API ​

在公共 API 中,该类型曾用于自定义 sequencers。请改用 TestSpecification。

onTestFinished 和 onTestFailed 现在接收上下文 ​

onTestFinished 和 onTestFailed 钩子以前接收测试结果作为第一个参数。现在,它们接收测试上下文,就像 beforeEach 和 afterEach 一样。

快照 API 的更改 API ​

@vitest/snapshot 中的公共快照 API 已更改,以支持单个运行中的多个状态。详情请参阅 PR:#6817

请注意,此更改仅影响直接使用快照 API 的开发人员。.toMatchSnapshot API 没有更改。

resolveConfig 类型签名的更改 API ​

resolveConfig 现在更有用。它现在接受用户配置而非已解析的 Vite 配置,并返回解析后的配置。

此函数不用于内部,仅作为公共 API 公开。

清理 vitest/reporters 类型 API ​

vitest/reporters 入口现在仅导出 reporter 实现和选项类型。如果您需要访问 TestCase/TestSuite 和其他任务相关类型,请从 vitest/node 额外导入它们。

即使 coverage.excludes 被覆盖,覆盖率也会忽略测试文件。 ​

通过覆盖 coverage.excludes 来将测试文件包含在覆盖率报告中已不再可能。测试文件现在总是被排除。

迁移到 Vitest 2.0 ​

默认池是 forks ​

Vitest 2.0 将 pool 的默认配置更改为 'forks',以提高稳定性。您可以在 PR 中阅读完整的动机。

如果您在未指定 pool 的情况下使用了 poolOptions,您可能需要更新配置:

ts
export default defineConfig({
  test: {
    poolOptions: {
      threads: { 
        singleThread: true, 
      }, 
      forks: { 
        singleFork: true, 
      }, 
    }
  }
})

钩子按堆栈运行 ​

在 Vitest 2.0 之前,所有钩子并行运行。在 2.0 中,所有钩子都串行运行。此外,afterAll/afterEach 钩子以相反的顺序运行。

要恢复钩子的并行执行,请将 sequence.hooks 更改为 'parallel':

ts
export default defineConfig({
  test: {
    sequence: { 
      hooks: 'parallel', 
    }, 
  },
})

suite.concurrent 并发运行所有测试 ​

以前,在套件上指定 concurrent 会将并发测试按套件分组并按顺序运行。现在,遵循 Jest 的行为,所有测试都并发运行(受 maxConcurrency 限制)。

V8 覆盖率的 coverage.ignoreEmptyLines 默认启用 ​

coverage.ignoreEmptyLines 的默认值现在为 true。这一重大变更可能影响代码覆盖率报告,某些项目需要调整覆盖率阈值。此调整仅影响当 coverage.provider 为 'v8' 时的默认设置。

移除 watchExclude 选项 ​

Vitest 使用 Vite 的观察器。通过将文件或目录添加到 server.watch.ignored 来排除:

ts
export default defineConfig({
  server: { 
    watch: { 
      ignored: ['!node_modules/examplejs'] 
    } 
  } 
})

--segfault-retry 已移除 ​

由于默认池的变更,此选项已不再需要。如果您遇到段错误,请尝试切换到 'forks' 池。如果问题仍然存在,请提交一个新问题并提供复现步骤。

套件任务中的空任务已移除 ​

这是对高级 任务 API 的更改。以前,遍历 .suite 最终会导向一个空的内部套件,该套件用于代替文件任务。

这使得 .suite 成为可选;如果任务在顶层定义,它将没有套件。您可以回退到现在所有任务(包括文件任务本身,因此请注意不要陷入无限递归)都存在的 .file 属性。

此更改还会从 expect.getState().currentTestName 中移除文件,并使 expect.getState().testPath 成为必需。

task.meta 已添加到 JSON Reporter ​

JSON reporter 现在为每个断言结果打印 task.meta。

简化模拟函数的泛型类型(例如 vi.fn<T>、Mock<T>) ​

以前 vi.fn<TArgs, TReturn> 分别接受两个泛型类型用于参数和返回值。现已改为直接接受函数类型 vi.fn<T> 以简化用法。

ts
import { vi } from 'vitest';
import type { Mock } from 'vitest';

const add = (x: number, y: number): number => x + y;

// using vi.fn<T>
const mockAdd = vi.fn<Parameters<typeof add>, ReturnType<typeof add>>(); 
const mockAdd = vi.fn<typeof add>(); 

// using Mock<T>
const mockAdd: Mock<Parameters<typeof add>, ReturnType<typeof add>> = vi.fn(); 
const mockAdd: Mock<typeof add> = vi.fn(); 

访问已解析的 mock.results ​

以前,当函数返回 Promise 时,Vitest 会解析 mock.results 值。现在有一个单独的 mock.settledResults 属性,它仅在返回的 Promise 被解析或拒绝时填充。

ts
const fn = vi.fn().mockResolvedValueOnce('result');
await fn();

const result = fn.mock.results[0]; // 'result'
const result = fn.mock.results[0]; // 'Promise<result>'

const settledResult = fn.mock.settledResults[0]; // 'result'

通过此更改,我们还引入了新的 toHaveResolved* 匹配器,类似于 toHaveReturned,以便在您之前使用 toHaveReturned 时更轻松地进行迁移:

ts
const fn = vi.fn().mockResolvedValueOnce('result');
await fn();

expect(fn).toHaveReturned('result'); 
expect(fn).toHaveResolved('result'); 

浏览器模式 ​

Vitest 浏览器模式在 Beta 阶段进行了大量变更。您可以在 GitHub 讨论页面 上阅读我们关于浏览器模式的理念。

大多数更改都是增量的,但也有一些小的破坏性更改:

  • none provider 已重命名为 preview #5842
  • preview provider 现在是默认值 #5842
  • indexScripts 已重命名为 orchestratorScripts #5842

已移除的弃用选项 ​

部分已弃用选项已被移除:

  • vitest typecheck 命令 - 请改用 vitest --typecheck
  • VITEST_JUNIT_CLASSNAME 和 VITEST_JUNIT_SUITE_NAME 环境变量(请改用 reporter 选项)
  • 检查 c8 覆盖率(请改用 coverage-v8)
  • 从 vitest 导出 SnapshotEnvironment - 请改从 vitest/snapshot 导入
  • SpyInstance 已移除,取而代之的是 MockInstance

迁移到 Vitest 1.0 ​

最低要求 ​

Vitest 1.0 要求 Vite 5.0 和 Node.js 18 或更高版本。

所有 @vitest/* 子包都需要 Vitest 1.0 版本。

快照更新 #3961 ​

快照中的引号不再转义,所有快照都使用反引号 (`),即使字符串只有一行。

  1. 引号不再转义:
diff
expect({ foo: 'bar' }).toMatchInlineSnapshot(`
  Object {
-    \\"foo\\": \\"bar\\",
+    "foo": "bar",
  }
`)
  1. 单行快照现在使用反引号 (`) 而不是单引号 (')。
diff
- expect('some string').toMatchInlineSnapshot('"some string"')
+ expect('some string').toMatchInlineSnapshot(`"some string"`)

@vitest/snapshot 包也有更改。如果您没有直接使用它,则无需更改任何内容。

  • 您不再需要仅仅为了覆盖 equalityCheck 方法而扩展 SnapshotClient:只需在实例化时将其作为 isEqual 传递即可。
  • client.setTest 已重命名为 client.startCurrentRun
  • client.resetCurrent 已重命名为 client.finishCurrentRun

池标准化 #4172 ​

我们移除了多项配置选项,以便更灵活地根据需求配置运行器。如果您依赖 --threads 或其他相关标志,请查看迁移示例。

  • --threads 现在是 --pool=threads
  • --no-threads 现在是 --pool=forks
  • --single-thread 现在是 --poolOptions.threads.singleThread
  • --experimental-vm-threads 现在是 --pool=vmThreads
  • --experimental-vm-worker-memory-limit 现在是 --poolOptions.vmThreads.memoryLimit
  • --isolate 现在是 --poolOptions.<pool-name>.isolate 和 browser.isolate
  • test.maxThreads 现在是 test.poolOptions.<pool-name>.maxThreads
  • test.minThreads 现在是 test.poolOptions.<pool-name>.minThreads
  • test.useAtomics 现在是 test.poolOptions.<pool-name>.useAtomics
  • test.poolMatchGlobs.child_process 现在是 test.poolMatchGlobs.forks
  • test.poolMatchGlobs.experimentalVmThreads 现在是 test.poolMatchGlobs.vmThreads
diff
{
  scripts: {
-    "test": "vitest --no-threads"
     // For identical behaviour:
+    "test": "vitest --pool forks --poolOptions.forks.singleFork"
     // Or multi parallel forks:
+    "test": "vitest --pool forks"

  }
}
diff
{
  scripts: {
-    "test": "vitest --experimental-vm-threads"
+    "test": "vitest --pool vmThreads"
  }
}
diff
{
  scripts: {
-    "test": "vitest --isolate false"
+    "test": "vitest --poolOptions.threads.isolate false"
  }
}
diff
{
  scripts: {
-    "test": "vitest --no-threads --isolate false"
+    "test": "vitest --pool forks --poolOptions.forks.isolate false"
  }
}

覆盖率更改 #4265, #4442 ​

coverage.all 选项现默认启用。这意味着所有匹配 coverage.include 模式的项目文件都将被处理,即使它们未被执行。

覆盖率阈值 API 的形状已更改,现在支持使用 glob 模式为特定文件指定阈值:

diff
export default defineConfig({
  test: {
    coverage: {
-      perFile: true,
-      thresholdAutoUpdate: true,
-      100: true,
-      lines: 100,
-      functions: 100,
-      branches: 100,
-      statements: 100,
+      thresholds: {
+        perFile: true,
+        autoUpdate: true,
+        100: true,
+        lines: 100,
+        functions: 100,
+        branches: 100,
+        statements: 100,
+      }
    }
  }
})

模拟类型 #4400 ​

为与 Jest 风格的“Mock”命名保持一致,部分类型已被移除。

diff
- import { EnhancedSpy, SpyInstance } from 'vitest'
+ import { MockInstance } from 'vitest'

WARNING

SpyInstance 已弃用,取而代之的是 MockInstance,并将在下一个主要版本中移除。

计时器模拟 #3925 ​

vi.useFakeTimers() 现已不再自动模拟 process.nextTick。 仍然可以通过使用 vi.useFakeTimers({ toFake: ['nextTick'] }) 显式指定来模拟 process.nextTick。

但是,当使用 --pool=forks 时,无法模拟 process.nextTick。如果您需要 process.nextTick 模拟,请使用不同的 --pool 选项。

从 Jest 迁移 ​

Vitest 设计了与 Jest 兼容的 API,旨在简化从 Jest 的迁移。尽管付出了这些努力,您仍然可能会遇到以下差异:

默认全局变量 ​

Jest 默认启用其全局 API。Vitest 不会。您可以选择通过全局配置设置启用全局变量,或者更新您的代码以使用从 vitest 模块导入。

如果您决定禁用全局变量,请注意像 testing-library 这样的常用库将不会自动进行 DOM 清理。

spy.mockReset ​

Jest 的mockReset会将模拟实现替换为返回 undefined 的空函数。

Vitest 的 mockReset 将模拟实现重置为其原始实现。 也就是说,重置由 vi.fn(impl) 创建的模拟会将模拟实现重置为 impl。

模块模拟 ​

在 Jest 中模拟模块时,工厂参数的返回值即为默认导出。在 Vitest 中,工厂参数必须返回一个明确定义每个导出的对象。例如,以下 jest.mock 必须更新如下:

ts
jest.mock('./some-path', () => 'hello') 
vi.mock('./some-path', () => ({ 
  default: 'hello', 
})) 

有关更多详细信息,请参阅 vi.mock API 部分。

自动模拟行为 ​

不同于 Jest,<root>/__mocks__ 中的模拟模块不会加载,除非调用 vi.mock()。如果您需要它们在每个测试中都被模拟,就像在 Jest 中一样,您可以在 setupFiles 中模拟。

导入模拟包的原始包 ​

若仅需部分模拟包,您可能曾使用 Jest 的 requireActual 函数。在 Vitest 中,您应该将这些调用替换为 vi.importActual。

ts
const { cloneDeep } = jest.requireActual('lodash/cloneDeep'); 
const { cloneDeep } = await vi.importActual('lodash/cloneDeep'); 

将模拟扩展到外部库 ​

Jest默认会这样处理:当模拟一个模块并希望此模拟扩展到使用相同模块的其他外部库时,您应该明确指出希望模拟哪个第三方库,以便外部库成为您的源代码的一部分,通过使用 server.deps.inline。

server.deps.inline: ["lib-name"]

expect.getState().currentTestName ​

Vitest的test名称使用>符号连接,便于区分测试和套件,而 Jest 使用空格 ()。

diff
- `${describeTitle} ${testTitle}`
+ `${describeTitle} > ${testTitle}`

环境变量 ​

与Jest类似,若NODE_ENV未设置,Vitest会将其设为test。Vitest 还有一个 JEST_WORKER_ID 的对应项,称为 VITEST_POOL_ID(总是小于或等于 maxThreads)。如果您依赖它,请不要忘记重命名。Vitest 还公开了 VITEST_WORKER_ID,它是正在运行的 worker 的唯一 ID。这个数字不受 maxThreads 的影响,并且会随着每个创建的 worker 而增加。

替换属性 ​

如需修改对象,在Jest中您会使用replaceProperty API。在 Vitest 中,您可以使用 vi.stubEnv 或 vi.spyOn 来实现相同的目的。

Done 回调 ​

自Vitest v0.10.0起,声明测试的回调样式已弃用。您可以将它们重写为使用 async/await 函数,或者使用 Promise 来模拟回调样式。

js
it('should work', (done) => {  
it('should work', () => new Promise(done => { 
  // ...
  done()
}) 
})) 

钩子 ​

beforeAll/beforeEach钩子在Vitest中可能返回teardown函数。因此,如果您的钩子返回除 undefined 或 null 之外的任何内容,您可能需要重写您的钩子声明:

ts
beforeEach(() => setActivePinia(createTestingPinia())) 
beforeEach(() => { setActivePinia(createTestingPinia()) }) 

在 Jest 中,钩子是按顺序调用的(一个接一个)。默认情况下,Vitest 并行运行钩子。要使用 Jest 的行为,请更新 sequence.hooks 选项:

ts
export default defineConfig({
  test: {
    sequence: { 
      hooks: 'list', 
    } 
  }
})

类型 ​

Vitest没有与jest命名空间等同的内容,因此需直接从vitest导入类型:

ts
let fn: jest.Mock<(name: string) => number>; 
import type { Mock } from 'vitest'; 
let fn: Mock<(name: string) => number>; 

计时器 ​

Vitest不支持Jest的旧版计时器。

超时 ​

若曾使用jest.setTimeout,需迁移至vi.setConfig:

ts
jest.setTimeout(5_000); 
vi.setConfig({ testTimeout: 5_000 }); 

Vue 快照 ​

此非Jest特有功能,但若曾使用带vue-cli预设的Jest,需安装jest-serializer-vue包,并在 setupFiles 中使用它:

js
import { defineConfig } from 'vite';

export default defineConfig({
  test: {
    setupFiles: ['./tests/unit/setup.js'],
  },
});
js
import vueSnapshotSerializer from 'jest-serializer-vue';

expect.addSnapshotSerializer(vueSnapshotSerializer);

否则您的快照将包含大量转义的引号 (") 字符。

Pager
上一页常见错误
下一页分析测试性能

基于 MIT 许可证 发布。

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

https://vitest.dev/guide/migration

基于 MIT 许可证 发布。

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