跳至内容

迁移指南

从 Vitest 0.34.6 迁移

最低要求

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 包。如果您没有直接使用它,则无需更改任何内容。

  • 您不再需要扩展 SnapshotClient 只是为了覆盖 equalityCheck 方法:只需在初始化实例时将其作为 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>.isolatebrowser.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'

警告

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 则没有。您可以通过 globals 配置设置 启用全局变量,或者更新您的代码以使用来自 vitest 模块的导入。

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

模块模拟

在 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"]

访问模拟 Promise 的返回值

Jest 和 Vitest 都将所有模拟调用的结果存储在 mock.results 数组中,其中每个调用的返回值都存储在 value 属性中。但是,当模拟或侦听 Promise 时(例如,使用 mockResolvedValue),在 Jest 中,value 属性将是一个 Promise,而在 Vitest 中,当 Promise 解析时,它将成为一个解析的值。

ts
await expect(spy.mock.results[0].value).resolves.toBe(123) 
expect(spy.mock.results[0].value).toBe(123) 

环境

与 Jest 相似,Vitest 会将 NODE_ENV 设置为 test,前提是它之前没有被设置。Vitest 还拥有 JEST_WORKER_ID 的对应项,名为 VITEST_POOL_ID(始终小于或等于 maxThreads),因此如果您依赖它,请不要忘记将其重命名。Vitest 还公开 VITEST_WORKER_ID,它是一个正在运行的工作进程的唯一 ID - 此数字不受 maxThreads 影响,并且会随着每个创建的工作进程而增加。

替换属性

如果您想修改对象,您将使用 Jest 中的 replaceProperty API,您也可以在 Vitest 中使用 vi.stubEnvvi.spyOn 来实现相同的功能。

完成回调

从 Vitest v0.10.0 开始,声明测试的回调风格已弃用。您可以将其重写为使用 async/await 函数,或使用 Promise 来模拟回调风格。

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

钩子

beforeAll/beforeEach 钩子可以在 Vitest 中返回 teardown 函数。因此,如果您返回的内容不是 undefinednull,则可能需要重写钩子声明。

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<string, [string]> 
import type { Mock } from 'vitest'
let fn: Mock<[string], string> 

此外,Vitest 使用 Args 类型作为第一个参数,而不是 Returns,如差异所示。

计时器

Vitest 不支持 Jest 的传统计时器。

超时

如果您使用的是 jest.setTimeout,则需要迁移到 vi.setConfig

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

Vue 快照

这不是 Jest 特有的功能,但如果您之前使用 Jest 与 vue-cli 预设,则需要安装 jest-serializer-vue 包,并在 setupFiles 中使用它。

vite.config.js

js
import {  } from 'vite'

export default ({
  : {
    : ['./tests/unit/setup.js']
  }
})

tests/unit/setup.js

js
import vueSnapshotSerializer from 'jest-serializer-vue'

expect.addSnapshotSerializer(vueSnapshotSerializer)

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