进阶 vitest.dev 2026-10-22 19:00:41 · 19 阅读

第16章 Vitest 测试环境:支持 Node、jsdom 与自定义扩展

测试环境 Vitest 提供 environment 选项,用于在特定环境中运行代码。你可以使用 environmentOptions 选项修改环境的行为。 默认情况下,你可以使用以下环境: node 是默认环境 jsdom 通过提供浏览器 API 来模拟浏览器环境,使用 jsdom 包 happy-dom 通过提供浏览器 API 来模拟浏览器环境,被认为比 jsdom 更快,但缺少某些 API,使用 happy-dom 包 edge-runtime 模拟 Vercel 的 edge-runtime,使用 @edge-runtime/vm 包 注意 在使用 jsdom 或 happy-dom 环境时,Vitest 遵循 Vite 在导入 CSS 和资源时相同的规则。如果导入外部依赖时出现未知扩展名 .css 错误,你需要通过将所有包添加到 server.deps.inline 来手动内联整个导入链。例如,如果在此导入链中 package-3 发生错误:源代码 -> package-1 -> package-2 -> package-3,你需要将这三个包都添加到 server.deps.inline。 外部依赖内部的 CSS 和资源的 require 会自动解析。 警告 “环境” 仅在 Node.js 中运行测试时存在。 browser 在 Vitest 中不被视为一种环境。如果你希望使用 Browser 模式运行部分测试,可以创建一个测试项目。 特定文件的环境 在配置中设置 environment 选项时,它将应用于项目中的所有测试文件。为了进行更精细的控制,你可以使用控制注释为特定文件指定环境。控制注释是以 @vitest-environment 开头并跟随环境名称的注释: ts // @vitest-environment jsdom import { expect, test } from 'vitest' test('test', () => { expect(typeof window).not.toBe('undefined') }) 自定义环境 你可以创建自己的包来扩展 Vitest 环境。为此,创建一个名为 vitest-environment-${name} 的包,或指定有效 JS/TS 文件的路径。该包应导出一个具有 Environment 形状的对象: ts import type { Environment } from 'vitest/runtime' export default { name: 'custom', viteEnvironment: 'ssr', // 可选 - 当 "setupVM" 较快时设置为 false,因此 vm 池 // 不会在运行时转换导入图 prewarmModules: true, // 可选 - 仅当你支持 "vmForks" 或 "vmThreads" 池时 async setupVM() { const vm = await import('node:vm') const context = vm.createContext() return { getVmContext() { return context }, teardown() { // 在此环境的所有测试运行后调用 } } }, setup() { // 自定义设置 return { teardown() { // 在此环境的所有测试运行后调用 } } } } 警告 Vitest 要求环境对象上设置 viteEnvironment 选项(默认回退到 Vitest 环境名称)。它应等于 ssr、client 或任何自定义 Vite 环境名称。此值确定使用哪个环境来处理文件。 你还可以通过 vitest/runtime 入口访问默认的 Vitest 环境: ts import { builtinEnvironments, populateGlobal } from 'vitest/runtime' console.log(builtinEnvironments) // { jsdom, happy-dom, node, edge-runtime } Vitest 还提供 populateGlobal 实用函数,可用于将属性从对象移动到全局命名空间: ts interface PopulateOptions { // 非类函数是否应绑定到全局命名空间 bindFunctions?: boolean } interface PopulateResult { // 所有已复制的键的列表,即使原始对象上没有该值 keys: Set // 属性描述符的映射,针对可能被覆盖的键 // 你可以在 `teardown` 内部使用 `Object.defineProperty` 恢复它们 originals: Map } export function populateGlobal(global: any, original: any, options: PopulateOptions): PopulateResult

评论 (0)