Vitest provide 配置详解用 inject 在主进程与测试进程间传递上下文【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitestprovide是 Vitest 提供的一项配置能力用于在主进程配置加载、globalSetup、编程式 API与各测试 worker 之间安全地传递一份「只读上下文」。本文以 docs/config/provide.md 为骨架结合仓库源码与 globalSetup、TestProject、Vitest API 等相关文档完整讲解provide的类型约束、序列化规则、TypeScript 类型增强以及它在 globalSetup、watch 模式与编程式 API 中的典型用法读完即可在项目中落地主进程与测试之间的数据传递方案。provide 配置项概述在 Vitest 配置中provide用于定义一组可以在测试内部通过inject方法访问的值。它的类型定义为Type:PartialProvidedContext即一个ProvidedContext的「部分可选」对象。ProvidedContext默认是一个空接口定义于 packages/vitest/src/types/general.ts#L30export interface ProvidedContext {}在 packages/vitest/src/node/types/config.ts#L838 中provide被声明为ResolvedConfig的可选字段provide?: PartialProvidedContext之所以使用Partial是因为你可以在一个项目中只提供部分上下文键其余键保持缺省。配置解析时packages/vitest/src/node/config/resolveConfig.ts#L286 会将其兜底为{}resolved.provide ?? {}这意味着即使你不配置provide该字段也始终是一个可用的空对象inject读取不到任何键时返回undefined不会抛错。基础用法配置 provide测试中用 inject 读取provide的完整闭环包含两端主进程写入、测试进程读取。第一步在vitest.config.js中配置provideimport { defineConfig } from vitest/config export default defineConfig({ test: { provide: { API_KEY: 123, }, }, })第二步在测试文件中通过inject读取import { expect, inject, test } from vitest test(api key is defined, () { expect(inject(API_KEY)).toBe(123) })inject从vitest入口导出其实现位于 packages/vitest/src/integrations/inject.tsexport function injectT extends keyof ProvidedContext string( key: T, ): ProvidedContext[T] { const workerState getWorkerState() return workerState.providedContext[key] as ProvidedContext[T] }可以看到inject只是从当前 worker 的全局状态workerState.providedContext中按 key 取值因此它天然是一个同步、廉价的读取操作适合在beforeAll、测试体甚至断言表达式中直接调用。一个更贴近实战的示例配置值不必是简单字符串只要可序列化对象、数组、布尔值都可以传递。例如传递一组接口基地址import { defineConfig } from vitest/config export default defineConfig({ test: { provide: { api: { baseUrl: https://api.example.com, version: v2, }, featureFlags: [billing, beta], retryCount: 3, }, }, })import { inject } from vitest const api inject(api) test(uses the configured base url, () { expect(api.baseUrl).toBe(https://api.example.com) })序列化约束跨进程传输的安全边界原文档对此给出了两条硬性约束必须在配置前牢记Properties have to be strings and values need to be serializable because this object will be transferred between different processes.即键key必须是字符串inject的类型签名T extends keyof ProvidedContext string也强制了这一点值必须可序列化该对象会在不同进程之间传输值必须满足 Web Workers 结构化克隆算法structured clone algorithm所支持的类型例如原始类型、普通对象、数组、Map、Set、Date、ArrayBuffer、RegExp等函数、Symbol、WeakMap等不可克隆的值不能放入。为什么要有这条约束从源码可以还原出这条完整的数据链路配置解析时resolveConfig.ts#L286 把provide归入解析后的配置调度任务时packages/vitest/src/node/pool.ts#L164 将project.getProvidedContext()放入 worker 任务上下文providedContext: project.getProvidedContext(),worker 启动时packages/vitest/src/runtime/worker.ts#L47 把它挂到 worker 全局状态上providedContext: ctx.providedContext,测试运行时inject从该状态取值。在这条链路中provide对象需要跨过主进程 → worker 进程forks/threadspool 通过 IPC 或结构化克隆传递。对于浏览器测试模式packages/vitest/src/node/pools/browser.ts#L256 甚至会先stringify整个上下文再注入页面所以不可序列化的值会在传输阶段直接失败。此外编程式 API 的provide方法还会在写入前主动用structuredClone做一次「可序列化校验」见 packages/vitest/src/node/project.ts#L124-L141provide T extends keyof ProvidedContext string( key: T, value: ProvidedContext[T], ): void { try { structuredClone(value) } catch (err) { throw new Error( Cannot provide ${key} because its not serializable., { cause: err }, ) } (this._provided as any)[key] value }也就是说一旦你传入不可克隆的值如函数会立刻得到明确的Cannot provide xxx because its not serializable.报错而不是等到 worker 端才出现难以排查的DataCloneError。注意校验只验证可克隆性存入的值本身并不会被克隆这一点同样适用于配置项形式的provide。TypeScript 类型增强让 inject 具备类型安全由于ProvidedContext默认是空接口直接使用inject(API_KEY)时类型会退化为never或报错。原文档给出的标准做法是通过「模块增强module augmentation」扩展该接口。创建vitest.shims.d.ts或任何被 tsconfig 包含的.d.ts文件declare module vitest { export interface ProvidedContext { API_KEY: string } } // mark this file as a module so augmentation works correctly export {}要点说明必须把ProvidedContext声明为接口interface而不是 type alias因为只有接口可以被声明合并文件末尾的export {}是必需的它把该文件标记为模块使declare module生效增强之后inject(API_KEY)的返回类型会被推断为string而传入不存在的键则会得到类型错误从而在编译期就拦截拼写错误。同理如果你在编程式 API 或 globalSetup 中使用了provide/project.provide也可以在vitest/node上做同样的增强见下文。在 globalSetup 中动态 provide运行期才知道的值配置项形式的provide适合「静态已知」的数据但很多值端口号、临时目录、动态生成的口令只有等 globalSetup 运行后才能确定。此时可以使用 globalSetup 接收到的TestProject实例上的provide方法。原文档在 docs/config/globalsetup.md 中给出了权威示例import type { TestProject } from vitest/node export default function setup(project: TestProject) { project.provide(wsPort, 3000) }import { inject } from vitest inject(wsPort) 3000对应的类型增强可以这样写文档同处给出declare module vitest { export interface ProvidedContext { wsPort: number } }为什么需要 provideglobalSetup 的全局作用域隔离globalSetup 运行在测试 worker 创建之前的另一个全局作用域中因此你在 setup 里定义的全局变量测试里根本访问不到。provide正是官方为此设计的「跨作用域数据桥」setup 进程中通过project.provide写入测试进程中通过inject读取。使用provide而非直接改全局变量的另一个好处是显式与可追踪测试通过inject显式声明自己依赖哪些上下文而不是隐式依赖一个不确定是否存在的全局变量。与 setupFiles 的取舍如果你需要执行与测试同进程的代码比如注入全局 mock、共享模块实例应改用setupFiles——它在每个测试文件运行前执行能直接访问与测试相同的全局作用域但也会为每个测试文件重复执行。provide/inject则专门解决「主进程数据 → 测试进程」的传递两者定位不同实践中常配合使用。编程式 APIcreateVitest 下的 provide在 Node 脚本中通过createVitest启动测试时vitest实例同样暴露provide它是vitest.getRootProject().provide的简写。来自 docs/api/advanced/vitest.mdimport { createVitest } from vitest/node const vitest await createVitest(test, { watch: false, }) vitest.provide(wsPort, 3000) declare module vitest { export interface ProvidedContext { wsPort: number } }测试侧读取方式完全一致import { inject } from vitest const port inject(wsPort) // 3000这里有一个值得注意的机制所有 project 都会继承根项目root project的 provided context。在 packages/vitest/src/node/project.ts#L146-L156 的getProvidedContext中可以看到合并逻辑getProvidedContext(): ProvidedContext { if (this.isRootProject()) { return this._provided } return { ...this.vitest.getRootProject().getProvidedContext(), ...this._provided, } }即每个项目最终拿到的上下文 根项目全局上下文 该项目自身写入的上下文后者覆盖前者同名键。因此vitest.provide(global, true)对所有项目可见project.provide(key, value)只对该特定项目可见在 docs/api/advanced/test-project.md 的示例中project.getProvidedContext()返回{ global: true, key: value }。动态更新的特性project.provide以及vitest.provide可以在测试运行期间动态调用已提供值会在测试下一次运行时生效无需重启进程。这在 watch 模式、长驻测试服务场景下非常实用。文档原文docs/api/advanced/test-project.md也明确说明 The values can be provided dynamically. Provided value in tests will be updated on their next run.一个完整的 Node 脚本示例import { createVitest } from vitest/node const vitest await createVitest(test, { watch: false, provide: { API_KEY: process.env.API_KEY, }, }) const project vitest.projects[0] project.provide(extra, { mode: ci }) await vitest.start() await vitest.close()典型应用场景综合上述用法provide/inject在真实项目中常见的落点包括场景提供方读取方WebSocket/HTTP 服务端口号globalSetup 中project.provide(wsPort, port)测试中inject(wsPort)数据库连接串、临时目录globalSetup 启动容器后动态提供需要真实连接的集成测试API Key、环境开关配置项test.provide所有测试基准测试参数如options配置项provide: { options }bench用例见 test/e2e/test/benchmarking.test.ts 中的inject(options)用法CI 上下文分支名、PR 号、执行器编号createVitest编程式注入断言或报告逻辑仓库的 e2e 测试中大量使用了这一模式例如 test/coverage-test/test/threshold-100.test.ts#L34 的const coverage inject(coverage)以及 test/browser/fixtures/inline-script/vite.config.ts#L22 中在配置里直接声明provide均可作为真实用法参考。小结provide配置项是 Vitest 主进程与测试进程之间传递数据的官方通道配合inject构成了一个「显式、可序列化、类型可选增强」的上下文体系。使用时记住三件事键必须为字符串值必须可结构化克隆——函数等不可序列化对象会在校验或传输阶段报错静态数据放配置项test.provide动态数据用 globalSetup 的project.provide或编程式 API 的vitest.provide用模块增强扩展ProvidedContext接口让inject的键与返回类型在编译期就受到保护。相关文档与实现可进一步阅读provide 配置、globalSetup、TestProject API、Vitest API以及实现层面的 inject 实现、provide 校验逻辑 和 worker 上下文注入。【免费下载链接】vitestNext generation testing framework powered by Vite.项目地址: https://gitcode.com/GitHub_Trending/vi/vitest创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考