Skip to content

experimental ​

experimental.openTelemetry 4.0.11+ ​

功能反馈

请将关于此功能反馈提交至 GitHub Discussion。

  • 类型:
ts
interface OpenTelemetryOptions {
  enabled: boolean
  /**
   * 暴露 Node.js OpenTelemetry SDK 的文件路径
   */
  sdkPath?: string
  /**
   * 暴露浏览器 OpenTelemetry SDK 的文件路径
   */
  browserSdkPath?: string
}
  • 默认值: { enabled: false }

此选项控制 OpenTelemetry 支持。当 enabled 设置为 true,Vitest 会在主线程中以及每个测试文件之前导入 SDK 文件。

性能警告

OpenTelemetry 可能会显著影响 Vitest 性能;建议仅在本地调试时启用它。

你可以将 自定义服务 与 Vitest 一起使用,以精确定位正在拖慢测试套件执行速度的测试或文件。

对于浏览器模式,请参阅 OpenTelemetry 指南的 浏览器模式 部分。

sdkPath 的路径解析相对于项目的 root 解析,应指向一个默认导出已初始化 SDK 实例的模块。例如:

js
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'
import { NodeSDK } from '@opentelemetry/sdk-node'

const sdk = new NodeSDK({
  serviceName: 'vitest',
  traceExporter: new OTLPTraceExporter(),
  instrumentations: [getNodeAutoInstrumentations()],
})

sdk.start()
export default sdk
js
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    experimental: {
      openTelemetry: {
        enabled: true,
        sdkPath: './otel.js',
      },
    },
  },
})

注意

请注意 Node 必须能够直接处理 sdkPath 指向的内容,因为它不会被 Vitest 转换。了解如何在 Vitest 中使用 OpenTelemetry ,详情参阅 指南。

experimental.importDurations 4.1.0+ ​

功能反馈

请将关于此功能反馈提交至 GitHub Discussion。

  • 类型:
ts
interface ImportDurationsOptions {
  /**
   * 何时在 CLI 终端打印导入耗时明细
   * - false: 从不打印(默认值)
   * - true: 总是打印
   * - 'on-warn': 仅当有导入超过警告阈值时打印
   */
  print?: boolean | 'on-warn'
  /**
   * 当任何导入超过危险阈值时使测试运行失败
   * 启用后若超过阈值,将始终打印明细
   * @default false
   */
  failOnDanger?: boolean
  /**
   * 收集并显示的最大导入数量
   */
  limit?: number
  /**
   * 持续时间阈值(毫秒),用于控制着色显示和警告触发。
   */
  thresholds?: {
    /** 黄色/警告颜色的阈值 @default 100 */
    warn?: number
    /** 红色/危险颜色及触发 failOnDanger 的阈值 @default 500 */
    danger?: number
  }
}
  • 默认值: { print: false, failOnDanger: false, limit: 0, thresholds: { warn: 100, danger: 500 } } (limit is 10 if print or UI is enabled)

配置导入耗时收集与显示功能。

print 选项控制 CLI 终端的输出行为,limit 选项控制收集和显示的导入数量上限。UI 模式 始终可切换明细显示视图(不受 print 设置影响)。

  • Self:模块导入耗时,不包括静态导入;
  • Total:模块导入耗时,包括静态导入。请注意,这不包括当前模块的 transform 时间。
终端中导入耗时明细的示例终端中导入耗时明细的示例

请注意,如果文件路径太长,Vitest 会从开头截断它,最多显示 45 个字符。

experimental.importDurations.print ​

  • 类型: boolean | 'on-warn'
  • 默认值: false

控制测试结束后何时在 CLI 打印导入耗时分析。该功能仅适用于 default、verbose 或 tree 报告器。

  • false: 从不打印分析结果
  • true: 总是打印分析结果
  • 'on-warn': 仅当任何导入耗时超过 thresholds.warn 阈值时打印

experimental.importDurations.failOnDanger ​

  • 类型: boolean
  • 默认值: false

当任何导入操作耗时超过 thresholds.danger 阈值时,测试将会运行失败。启用该选项且超过阈值时,无论 print 如何设置,始终会打印性能分析报告。

该功能适用于在 CI 环境中确保导入操作符合性能预期:

bash
vitest --experimental.importDurations.failOnDanger

experimental.importDurations.limit ​

  • 类型: number
  • 默认值: 0(如果启用 print、failOnDanger 或 UI 模式时默认为10)

在 CLI 输出、UI 模式 及第三方报告器中收集和显示的导入操作最大数量限制。

experimental.importDurations.thresholds ​

  • 类型: { warn?: number; danger?: number }
  • 默认值: { warn: 100, danger: 500 }

用于着色和警告的耗时阈值(单位:毫秒):

  • warn:触发黄色/警告颜色的阈值(默认值:100毫秒)
  • danger:触发红色/危险颜色及 failOnDanger 的阈值(默认值:500毫秒)

说明

UI 模式 会在至少一个文件的加载时间超过 danger 阈值时,自动显示导入耗时分析。

experimental.viteModuleRunner 4.1.0+ ​

反馈

通过 GitHub 讨论区 提交关于此功能的反馈。

  • 类型: boolean
  • 默认值: true

控制 Vitest 是否使用 Vite 的 模块运行器 执行代码,或回退至原生 import 方式。

如果在顶级配置中定义此选项,所有 项目 将自动继承该设置。

当测试运行环境与代码执行环境相同时(例如服务端后端或简单脚本),可考虑禁用模块运行器。但对于 jsdom/happy-dom 测试,我们仍建议使用 Vite 模块运行器或在 浏览器模式 中运行,因为这样无需添加额外的配置。

禁用此选项将导致 所有 文件转换失效:

  • 测试文件及源码不会经过 Vite 处理
  • 全局初始化文件不会被处理
  • 自定义运行器 / 池 / 环境文件不会被处理
  • 配置文件仍由 Vite 处理(该过程发生在 Vitest 获知 viteModuleRunner 参数之前执行)

注意

当前 Vitest 仍需依赖 Vite 实现某些功能,如模块图或监视模式。

另外请注意,此选项仅适用于forks或threads执行池。

模块运行器 ​

Vitest 默认在由 Vite 环境 API 提供非常宽松的模块运行器沙箱中执行测试。所有文件被归类为 "inline" 模块或 "external" 模块。

模块运行器负责执行所有 "inline" 模块。它提供 import.meta.env 环境变量、require 函数、__dirname 和 __filename 路径变量、静态 import 语法,并具备独立的模块解析机制。这使得在不配置环境的情况下运行代码变得非常简单,只需要测试你编写的纯 JavaScript 逻辑是否按预期工作即可。

所有 "external" 模块均以原生模式运行,这意味着它们会在模块运行器的沙箱环境之外执行。当在 Node.js 环境中运行测试时,这些文件将通过原生 import 关键字导入,并由 Node.js 直接处理。

虽然在宽松的模拟环境中运行 JSDOM / happy-dom 测试可能具有合理性,但在非 Node.js 环境下运行 Node.js 测试可能会隐藏并抑制你在生产环境中可能遇到的潜在错误,尤其是当你的代码不需要 Vite 插件提供的任何额外转换时。

已知限制 ​

部分 Vitest 功能依赖于文件转换机制。Vitest 采用同步的 Node.js Loaders API 来转换测试文件和配置文件,以实现以下功能支持:

注意

这意味着 Vitest 至少需要 Node 22.15 才能使这些功能工作。目前,它们也不适用于 Deno 或 Bun。

Vitest 只会在测试文件中检测 vi.mock 和 vi.hoisted,它们不会在导入的模块中被提升。

这可能会影响性能,因为 Vitest 需要读取文件并处理它。如果你不使用这些功能,可以通过将 experimental.nodeLoader 设置为 false 来禁用转换。Vitest 只在查找 vi.mock 或 vi.hoisted 时读取测试文件和全局初始化文件。在其他文件中使用它们不会将它们提升到文件顶部,并可能导致意外行为。

由于 viteModuleRunner 的性质,某些功能将不起作用,包括:

  • 不支持 import.meta.env:import.meta.env 是 Vite 特性,使用 process.env 替代
  • 不支持 plugins:由于不存在转换阶段,插件不会生效,通过 execArgv 使用 自定义钩子 替代
  • 不支持 alias:由于不存在转换阶段,路径别名不会生效
  • istanbul 覆盖率工具无法工作(因缺少转换阶段),请改用 v8 覆盖率工具
    • vi.resetModules():没有 API 可以从模块缓存中使 ES 模块失效there

覆盖率支持

当前 Vitest 通过 v8 提供程序支持覆盖率分析,前提是文件能够被转换为 JavaScript。对于 TypeScript 转换,Vitest 使用 Node.js v22.13 版本起提供的 module.stripTypeScriptTypes 功能。如果你使用自定义 模块加载器,Vitest 将无法复用该加载器进行覆盖率分析所需的文件转换。

关于模拟对象功能,需要特别指出的是 ES 模块不支持属性重写。这意味着以下代码将无法正常工作:

ts
import * as fs from 'node:fs'
import { vi } from 'vitest'

vi.spyOn(fs, 'readFileSync').mockImplementation(() => '42') // ❌

但 Vitest 支持在不覆盖模块实现的情况下进行自动监听。当调用 vi.mock 并传入 spy: true 参数时,模块会以保留原始实现的方式被模拟,同时所有导出的函数都会被包裹在 vi.fn() 监听器中:

ts
import * as fs from 'node:fs'
import { vi } from 'vitest'

vi.mock('node:fs', { spy: true })

fs.readFileSync.mockImplementation(() => '42') // ✅

工厂模拟功能通过顶层 await 实现。这意味着在你的源代码中无法使用 require() 加载被模拟的模块:

ts
vi.mock('node:fs', async (importOriginal) => {
  return {
    ...await importOriginal(),
    readFileSync: vi.fn(),
  }
})

const fs = require('node:fs') // 报错

这种限制源自于工厂函数可以是异步的。不过这不构成问题,因为 Vitest 默认不会模拟 node_modules 中的内置模块,这类似于 Vitest 默认的工作方式。

TypeScript ​

如果你使用的是 Node.js 22.18/23.6 或更高版本,TypeScript 将由 Node.js 原生支持转换。

在 Node.js 22.6-22.18 环境中的使用 TypeScript

如果你使用的 Node.js 版本介于 22.6 至 22.18 之间,还可通过 --experimental-strip-types 参数启用原生 TypeScript 支持:

shell
NODE_OPTIONS="--experimental-strip-types" vitest

如果你使用的是 TypeScript 且 Node.js 版本低于 22.6,则需要执行以下任一操作:

  • 构建测试文件和源代码并直接运行这些文件
  • 通过 execArgv 参数导入 自定义加载器
ts
import { defineConfig } from 'vitest/config'

const tsxApi = import.meta.resolve('tsx/esm/api')

export default defineConfig({
  test: {
    execArgv: [
      `--import=data:text/javascript,import * as tsx from "${tsxApi}";tsx.register()`,
    ],
    experimental: {
      viteModuleRunner: false,
    },
  },
})

如果你在 Deno 中运行测试,TypeScript 文件由运行时处理,无需任何额外配置。

experimental.vcsProvider 4.1.1+ ​

  • 类型: VCSProvider | string
ts
interface VCSProvider {
  findChangedFiles(options: VCSProviderOptions): Promise<string[]>
}

interface VCSProviderOptions {
  root: string
  changedSince?: string | boolean
}
  • 默认值: 'git'

用于检测更改文件的自定义驱动。与 --changed 参数配合使用,用于确定哪些文件已被修改。

默认情况下,Vitest 使用 Git 检测更改的文件。你可以提供 VCSProvider 接口的自定义实现以使用不同的版本控制系统:

vitest.config.ts
ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    experimental: {
      vcsProvider: {
        async findChangedFiles({ root, changedSince }) {
          // 返回已变更的文件路径
          return []
        },
      },
    },
  },
})

你也可以传入一个字符串路径,指向包含实现 VCSProvider 接口的默认导出模块:

vitest.config.js
js
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    experimental: {
      vcsProvider: './my-vcs-provider.js',
    },
  },
})
my-vcs-provider.js
js
export default {
  async findChangedFiles({ root, changedSince }) {
    // 返回已变更的文件路径
    return []
  },
}

experimental.nodeLoader 4.1.0+ ​

  • 类型: boolean
  • 默认值: true

如果禁用模块运行器,Vitest 会使用原生 Node.js 模块加载器 来转换文件,以支持 import.meta.vitest、vi.mock 和 vi.hoisted 功能。

如果你不使用这些特性,可禁用此功能以提升性能。

experimental.preParse 4.1.3+ ​

  • 类型: boolean
  • 默认值: false

在运行测试前预解析 TestSpecification。这会在所有文件中应用 .only 修饰符、-t 测试名称模式、--tags-filter、测试行号 和 测试ID 而无需执行它们。例如,如果只有一个测试标记了 .only,Vitest 将跳过所有其他文件中的测试。

提示

使用 .only、-t 参数或 --tags-filter 时推荐启用此选项。

无条件启用可能会因额外的解析步骤而降低测试运行速度。

注意

预解析使用静态分析(AST 解析)而非执行测试文件。这意味着测试名称、标签和修饰符(.only、.skip、.todo)必须可静态分析的。动态测试名称(例如,存储在变量中或从函数调用返回的名称)和非字面量标签将无法正确解析。

ts
// ✅ 有效 —— 静态字符串字面量
test('adds numbers', () => {})

// ✅ 有效 —— 静态标签
test('my test', { tags: ['unit'] }, () => {})

// ❌ 无法正确匹配 —— 动态名称
const name = getName()
test(name, () => {})

// ❌ 无法正确匹配 —— 动态标签
const tags = getTags()
test('my test', { tags }, () => {})