Skip to content

Vitest 4.0 发布

2025 年 10 月 22 日

Vitest 4 公告封面图

下一代 Vitest 主版本来了

今天,我们激动地宣布 Vitest 4!

快速链接:

如果你之前没用过 Vitest,我们建议先阅读 入门特性 指南。

我们向 640 多位 Vitest Core 贡献者 以及帮助我们开发这个新主版本的 Vitest 集成、工具和翻译的维护者和贡献者表示感谢。我们鼓励你参与进来,帮助我们在整个生态系统中改进 Vitest。了解更多请访问我们的 贡献指南

为了开始,我们建议帮助 分类问题审查 PR,基于未解决的问题发送失败测试的 PR,并在 讨论区 和 Vitest Land 的 帮助论坛 中支持他人。如果你想与我们交流,加入我们的 Discord 社区 并在 #contributing 频道 打招呼。

有关 Vitest 生态系统和 Vitest core 的最新消息,请在 BlueskyMastodon 上关注我们。

为了保持更新,请关注 VoidZero 博客 并订阅 通讯

浏览器模式已稳定

随着此次发布,我们从 浏览器模式 中移除了 experimental 标签。为了实现这一点,我们必须对公共 API 引入一些变更。

要定义提供者,你现在需要安装一个独立的包:@vitest/browser-playwright@vitest/browser-webdriverio@vitest/browser-preview。这使得使用自定义选项更简单,并且不再需要添加 /// <reference 注释。

ts
import { defineConfig } from 'vitest/config'
import { playwright } from '@vitest/browser-playwright'
/// <reference path="@vitest/browser/providers/playwright" />

export default defineConfig({
  test: {
    browser: {
      provider: 'playwright', 
      provider: playwright({ 
        launchOptions: { 
          slowMo: 100, 
        }, 
      }), 
      instances: [
        {
          browser: 'chromium',
          launch: { 
            slowMo: 100, 
          }, 
        },
      ],
    },
  },
})
ts
import { defineConfig } from 'vitest/config'
import { webdriverio } from '@vitest/browser-webdriverio'
/// <reference path="@vitest/browser/providers/webdriverio" />

export default defineConfig({
  test: {
    browser: {
      provider: 'webdriverio', 
      provider: webdriverio({ 
        capabilities: { 
          browserVersion: '82', 
        }, 
      }),
      instances: [
        {
          browser: 'chrome',
          capabilities: { 
            browserVersion: '82', 
          }, 
        },
      ],
    },
  },
})
ts
import { defineConfig } from 'vitest/config'
import { preview } from '@vitest/browser-preview'

export default defineConfig({
  test: {
    browser: {
      provider: 'preview', 
      provider: preview(), 
      instances: [
        { browser: 'chrome' },
      ],
    },
  },
})

context 不再从 @vitest/browser/context 导入(但它将继续工作直到下一个主版本,以便与尚未更新的工具更好地兼容),现在只需从 vitest/browser 导入:

ts
import { page } from '@vitest/browser/context'
import { page } from 'vitest/browser'

test('example', async () => {
  await page.getByRole('button').click()
})

通过这些变更,@vitest/browser 包可以从你的依赖中移除。它现在会自动包含在每个提供者包中。

视觉回归测试

Vitest 4 在浏览器模式中添加了 视觉回归测试 支持。我们将继续迭代此功能以改进体验。

Vitest 中的视觉回归测试可以通过 toMatchScreenshot 断言 完成:

ts
import { expect, test } from 'vitest'
import { page } from 'vitest/browser'

test('hero section looks correct', async () => {
  // ... 测试的其余部分

  // 捕获并比较截图
  await expect(page.getByTestId('hero')).toMatchScreenshot('hero-section')
})

Vitest 捕获你的 UI 组件和页面的截图,然后将它们与参考图像进行比较以检测意外的视觉变化。

除了此功能外,Vitest 还引入了 toBeInViewport 匹配器。它允许你使用 IntersectionObserver API 检查元素当前是否在视口中。

ts
// 特定元素在视口中。
await expect.element(page.getByText('Welcome')).toBeInViewport()

// 特定元素的 50% 应该在视口中
await expect.element(page.getByText('To')).toBeInViewport({ ratio: 0.5 })

Playwright Traces 支持

Vitest 4 支持生成 Playwright Traces。要启用追踪,你需要在 test.browser 配置中设置 trace 选项,或传递 --browser.trace=on 选项(offon-first-retryon-all-retriesretain-on-failure 也可用)。

Playwright Traces 界面

追踪在报告器中作为 注解 可用。例如,在 HTML 报告器中,你可以在测试详情中找到追踪文件的链接。要打开追踪文件,你可以使用 Playwright Trace Viewer

Locator 改进

frameLocator 方法返回一个 FrameLocator 实例,可用于查找 iframe 内部的元素。 Vitest 现在支持一个新的 page.frameLocator API(仅与 playwright 提供者一起使用)。

ts
const frame = page.frameLocator(
  page.getByTestId('iframe')
)

await frame.getByText('Hello World').click() // ✅
await frame.click() // ❌ 不可用

每个 locator 现在都暴露一个 length 属性,允许它们与 toHaveLength 匹配器自动一起使用:

ts
await expect.element(page.getByText('Item')).toHaveLength(3)

改进的调试

vscode 扩展 现在在运行浏览器测试时支持 "调试测试" 按钮。

如果你更喜欢自己配置调试选项,你可以使用 --inspect 标志启动 Vitest(playwrightwebdriverio 可用),并手动连接到 DevTools。在这种情况下,Vitest 还将自动禁用新的 trackUnhandledErrors 选项。

类型感知 Hooks

当使用 test.extend 与生命周期 hooks(如 beforeEachafterEach)时,你现在可以直接在返回的 test 对象上引用它们:

ts
import { test as baseTest } from 'vitest'

const test = baseTest.extend<{
  todos: number[]
}>({
  todos: async ({}, use) => {
    await use([])
  },
})

// 与全局 hooks 不同,这些 hooks 感知扩展的 context
test.beforeEach(({ todos }) => {
  todos.push(1)
})

test.afterEach(({ todos }) => {
  console.log(todos)
})

expect.assert

Vitest 一直导出 Chai 的 assert,但有时使用它很不方便,因为许多模块都有相同的导出。

现在 Vitest 在 expect 上暴露了相同的方法以便于访问。如果你需要缩小类型范围,这尤其有用,因为 expect.to* 方法不支持这一点:

ts
interface Cat {
  __type: 'Cat'
  mew(): void
}
interface Dog {
  __type: 'Dog'
  bark(): void
}
type Animal = Cat | Dog

const animal: Animal = { __type: 'Dog', bark: () => {} }

expect.assert(animal.__type === 'Dog')
// 不会显示类型错误!
expect(animal.bark()).toBeUndefined()

expect.schemaMatching

Vitest 4 引入了一种新的非对称匹配器,称为 expect.schemaMatching。它接受一个 Standard Schema v1 对象并针对它验证值,当值符合 schema 时通过断言。

提醒一下,非对称匹配器可用于所有检查相等性的 expect 匹配器中,包括 toEqualtoStrictEqualtoMatchObjecttoContainEqualtoThrowtoHaveBeenCalledWithtoHaveReturnedWithtoHaveBeenResolvedWith

ts
import { expect, test } from 'vitest'
import { z } from 'zod'
import * as v from 'valibot'
import { type } from 'arktype'

test('邮箱验证', () => {
  const user = { email: 'john@example.com' }

  // 使用 Zod
  expect(user).toEqual({
    email: expect.schemaMatching(z.string().email()),
  })

  // 使用 Valibot
  expect(user).toEqual({
    email: expect.schemaMatching(v.pipe(v.string(), v.email()))
  })

  // 使用 ArkType
  expect(user).toEqual({
    email: expect.schemaMatching(type('string.email')),
  })
})

报告器更新

移除了 basic 报告器。你可以改用 summary: falsedefault 报告器:

ts
export default defineConfig({
  test: {
    reporters: [
      ['default', { summary: false }],
    ],
  },
})

default 报告器现在仅在只有一个测试文件运行时才以树形结构打印测试。如果你希望始终看到测试以树形结构打印,可以使用新的 tree 报告器。

verbose 报告器现在总是在测试完成时逐个打印。此前,这仅在 CI 中完成,而在本地 verbose 的行为大多类似于 default 报告器。如果你希望保留旧的行为,可以通过更新配置,仅在 CI 中条件性地使用 verbose 报告器:

ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    reporter: process.env.CI ? 'verbose' : 'default',
  },
})

新的 API 方法

Vitest 4 带来了新的高级公共 API 方法

破坏性变更

Vitest 4 有一些可能会影响你的破坏性变更,因此我们建议在升级前查阅详细的 迁移指南

完整的变更列表位于 Vitest 4 变更日志

致谢

Vitest 4 是 Vitest 团队 和我们的贡献者无数小时工作的成果。我们感谢赞助 Vitest 开发的个人和公司。VladimirHiroshiVoidZero 团队的一部分,能够全职从事 Vite 和 Vitest 的工作,而 Ari 得益于 StackBlitz 可以在 Vitest 上投入更多时间。特别感谢 NuxtLabsZammad 以及 Vitest 的 GitHub SponsorsVitest 的 Open Collective 上的赞助商。