从 Vitest 迁移
如果你正在使用 Rstack 工具链(Rsbuild / Rslib / Rspack 等),迁移到 Rstest 可以带来更一致的开发体验。
使用 Agent Skills
如果你在使用支持 Skills 的 Coding Agent,可以安装 migrate-to-rstest 技能来辅助完成从 Vitest 到 Rstest 的迁移。
安装后,让 Coding Agent 协助完成升级即可。
安装依赖
首先,你需要安装 Rstest 依赖。
接下来,更新 package.json 中的测试脚本,使用 rstest 替代 vitest。例如:
rstest 没有 --run 参数。直接运行 rstest 就会执行一次测试并退出;如果你想使用 watch 模式,可以加上 --watch:
CLI 参数映射
Vitest 的一部分 CLI 参数可以直接映射到 Rstest,另一部分则需要调整写法。迁移时,最常遇到的差异可以参考下表:
配置迁移
将你的 Vitest 配置文件(例如 vite.config.ts 或 vitest.config.ts)迁移为 rstest.config.ts:
Helper 映射
Vitest 配置文件中使用的 helper 可以对应到 @rstest/core 导出的同类方法:
Vitest 配置映射
迁移配置时,重点关注这两点:
- 移除
test字段,将其内部配置提升到顶层。 - 一些字段名的调整(例如
test.environment→testEnvironment)。
请遍历 test 下的每一个字段,对照下表进行提升、重命名或删除。表中未列出的字段未必能 1:1 映射,直接删除前请先对照 Rstest 配置参考 确认。
文件级环境注释
如果你的 Vitest 测试使用了文件级环境注释,Rstest 在迁移时会识别 Vitest 环境注释:
你可以保留这些注释,也可以改名为 @rstest-environment / @rstest-environment-options。选项值必须是单行 JSON 对象。支持的环境包括 node、jsdom 和 happy-dom。
编译配置
Rstest 使用 Rsbuild 作为默认测试编译工具,而不是 Vite。你可以在 Build Configurations 查看全部编译配置项。
Rstest 采用基于 bundle 的执行模型来运行测试。没有进入 bundle graph 的代码会保留 Node.js 原生 loader 行为,这可能和 Vitest 不同。如果你在运行时动态加载文件,请参考 bundle graph 之外的代码使用 Node.js 原生行为。
大部分项目中,主要的编译侧变化如下:
- 使用
source.define替代define。 - 使用
output.externals替代ssr.external。 - 使用 Rsbuild 插件替代 Vite 插件。
如果你使用的是 Rslib 或 Rsbuild,也可以直接复用对应配置:
- Rslib 项目(存在
rslib.config.*)使用@rstest/adapter-rslib,并在extends中配置withRslibConfig()(参考 Rslib 集成文档)。 - Rsbuild 项目(存在
rsbuild.config.*)使用@rstest/adapter-rsbuild,并在extends中配置withRsbuildConfig()(参考 Rsbuild 集成文档)。
更新测试 API
测试 API
Rstest 提供了与 Vitest 兼容的 API,已有的 Vitest 测试文件通常只需要极少改动。将 vitest 的导入替换为 @rstest/core,并把 vi / vitest 工具 API 替换为对应的 rs / rstest:
完整工具 API 请参考 Rstest APIs。
全局 API
当启用 globals: true 时,Vitest 会把 vi 和 vitest 挂在全局对象上。在 Rstest 中,建议按以下顺序映射:
vi.<api>→rs.<api>vitest.<api>→rs.<api>
rs 和 rstest 是等价的全局别名,但统一使用 rs 可以让迁移后的示例和 import style 保持一致。
从 @rstest/core 导入 API 时,统一使用 import style 的 rs.<api> 更一致,避免在同一文件里与 global style 混用。
Setup adapter
有些 setup adapter 是 Vitest 专用的。例如 @testing-library/jest-dom/vitest 面向 Vitest;在 Rstest 中通过 expect.extend 直接注册 matcher。
路径解析
new URL('./file', import.meta.url) 会相对于源码模块解析,与 Node.js 和 Vitest 行为一致,因此可以在 setup 或 helper 文件中定位同级文件(资源、fixture):
Rstest 会保留这类表达式,而不会将其改写成打包后的资源路径,因此解析得到的 URL 指向磁盘上的源文件。
由于这类表达式不会进入打包器的依赖图,在 watch 模式下修改此类同级文件不会自动重跑在运行时读取它的测试。
自动模拟模块
Vitest 和 Rstest 都支持在 mock 调用只包含模块路径时自动 mock。Rstest 会先尝试从对应 __mocks__ 目录加载手写 mock;如果没找到,再自动 mock 整个模块,把函数导出替换为空 mock 函数。
该行为从 Rstest 0.11.0 开始支持。更早版本中,如果没有手写 mock,需要显式传入 { mock: true } 来请求自动 mock。
如果你想跳过 __mocks__ 查找并直接请求自动 mock,可以显式传入 { mock: true }:
Mock 异步模块
当你需要 mock 模块返回值时,Rstest 不支持返回异步函数。
作为替代,Rstest 提供了同步 importActual 能力,你可以通过静态 import 导入未 mock 的真实实现:
mock factory 是 hoisted 执行的;依赖同模块中后初始化的变量会触发初始化顺序错误。共享值可放到 hoisted initializer(例如 rs.hoisted(...))中规避。
Snapshot
Vitest 和 Rstest 使用相同的 snapshot key 格式和 body 序列化方式。原有的 __snapshots__/*.snap 文件可以被 Rstest 原样读取;在 Vitest 下能通过的测试,到 Rstest 下也能通过,不需要重录。两者只有文件 header 行不同:
执行 rstest -u 会把 header 规整为 Rstest 形式,snapshot body 保持 byte-identical。