For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt, and this page is available as Markdown at /zh/guide/migration/vitest.md.
close
  • 简体中文
  • 从 Vitest 迁移

    如果你正在使用 Rstack 工具链(Rsbuild / Rslib / Rspack 等),迁移到 Rstest 可以带来更一致的开发体验。

    使用 Agent Skills

    如果你在使用支持 Skills 的 Coding Agent,可以安装 migrate-to-rstest 技能来辅助完成从 Vitest 到 Rstest 的迁移。

    npx skills add rstackjs/agent-skills --skill migrate-to-rstest

    安装后,让 Coding Agent 协助完成升级即可。

    安装依赖

    首先,你需要安装 Rstest 依赖。

    npm
    yarn
    pnpm
    bun
    deno
    npm add @rstest/core -D

    接下来,更新 package.json 中的测试脚本,使用 rstest 替代 vitest。例如:

    "scripts": {
    -  "test": "vitest run" // 或 "vitest --run"
    +  "test": "rstest"
    }

    rstest 没有 --run 参数。直接运行 rstest 就会执行一次测试并退出;如果你想使用 watch 模式,可以加上 --watch

    "scripts": {
    -  "test": "vitest"
    +  "test": "rstest --watch"
    }

    CLI 参数映射

    Vitest 的一部分 CLI 参数可以直接映射到 Rstest,另一部分则需要调整写法。迁移时,最常遇到的差异可以参考下表:

    Vitest CLI 参数Rstest 对应写法说明
    vitest run / vitest --runrstest没有 --run 参数 —— 运行 rstest 默认就是执行一次。
    vitest / vitest watch / vitest --watchrstest --watchrstest watchRstest 不会自动进入 watch 模式,需要显式加 --watch
    vitest --coveragerstest --coverage还需安装与你配置对应的 provider 包(详见 coverage 配置行)。
    vitest --environment=jsdomrstest --testEnvironment jsdom
    vitest --reporter=verboserstest --reporters verbose
    vitest --globalsrstest --globals
    vitest -t <pattern> / --testNamePatternrstest -t <pattern> / --testNamePattern
    vitest -u / --updaterstest -u / --update
    vitest -c <path> / --configrstest -c <path> / --config
    vitest --project <name>rstest --project <name>

    配置迁移

    将你的 Vitest 配置文件(例如 vite.config.tsvitest.config.ts)迁移为 rstest.config.ts

    rstest.config.ts
    import { defineConfig } from '@rstest/core';
    
    export default defineConfig({
      // 根据下方映射表,从 vitest.config.ts 中逐字段迁移到这里。
    });

    Helper 映射

    Vitest 配置文件中使用的 helper 可以对应到 @rstest/core 导出的同类方法:

    Vitest(vitest/configRstest(@rstest/core说明
    defineConfigdefineConfig
    defineProjectdefineProjectprojects 数组内使用对象形式时,优先使用 defineInlineProject —— 它要求显式声明 name
    defineWorkspace移除没有独立的 workspace helper。直接在 defineConfigprojects 字段内声明各 project —— 详见 Projects
    mergeConfigmergeRstestConfig会进行 deep merge 并正确处理函数类型字段。在 projects 数组内组合单个 project 配置时,使用 mergeProjectConfig

    Vitest 配置映射

    迁移配置时,重点关注这两点:

    • 移除 test 字段,将其内部配置提升到顶层。
    • 一些字段名的调整(例如 test.environmenttestEnvironment)。

    请遍历 test 下的每一个字段,对照下表进行提升、重命名或删除。表中未列出的字段未必能 1:1 映射,直接删除前请先对照 Rstest 配置参考 确认。

    Vitest(test 下)Rstest(顶层)说明
    environmenttestEnvironmenttest.environmentOptions 合并到对象形式:testEnvironment: { name: 'jsdom', options: { ... } }。不支持自定义 environment 包。
    include / excludeinclude / exclude
    includeSourceincludeSource
    setupFilessetupFiles
    globalSetupglobalSetupRstest 调用 setup 时不传参数 —— 如果你的 Vitest setup 读取了 TestProject 参数(如 provideonTestsRerun),迁移时需重写。Vitest 的 provide / inject 没有直接等价形式 —— 在 setup 里修改 process.env(Rstest 会在 setup 结束后快照并注入每个 worker),或使用 env 配置字段传递静态值。
    globalsglobals
    namename
    rootroot
    envenv
    aliasresolve.aliasRstest 下不是 test.* 字段 —— 迁到顶层 resolve.alias
    passWithNoTestspassWithNoTests
    isolateisolate
    testTimeout / hookTimeouttestTimeout / hookTimeout
    teardownTimeout移除没有对等字段 —— Vitest 的 teardownTimeout 是 shutdown 等待超时,与 Rstest 的 hookTimeout(生命周期 hook)无关。
    slowTestThresholdslowTestThreshold
    maxConcurrencymaxConcurrency限制单个测试文件内 test.concurrent / describe.concurrent 用例的并发数。要限制同时运行的测试文件数量,请改用 pool.maxWorkers
    fileParallelism: falsepool.maxWorkers: 1Rstest 没有 boolean 形式的 fileParallelism 开关。设置 pool.maxWorkers1,即可让测试文件逐个运行。
    retryretry
    bailbail
    clearMocksclearMocks
    mockResetresetMocks
    restoreMocksrestoreMocks
    poolOptions.forks.maxForkspool.maxWorkersRstest 把 poolOptions 摊平到顶层 poolpoolOptions.forks.maxForkspool.maxWorkersexecArgvpool.execArgvminForks 没有对应项 —— Rstest 不提供 minWorkers 配置。只支持 forks pool —— pool: 'threads' | 'vmThreads' | 'vmForks' 这些配置会被丢弃(行为回退到 forks)。Vitest 4 的顶层 test.maxWorkers 映射到 pool.maxWorkerstest.minWorkers 会被丢弃)。
    coveragecoverageRstest 同时支持 provider: 'istanbul'provider: 'v8'。把 @vitest/coverage-v8 换成 @rstest/coverage-v8,或把 @vitest/coverage-istanbul 换成 @rstest/coverage-istanbul。把 coverage.reporter 改为 coverage.reporters(单数写法会被静默忽略)。下列子字段 1:1 对应:includeexcludereportsDirectorythresholds。Vitest 特有且没有对等字段的配置仍需删除,例如 allskipFullthresholdAutoUpdateprocessingConcurrencycustomProviderModulewatermarksignoreClassMethods 等。
    reportersreportersVitest 独有(需替换或丢弃):taptap-flathtmltreehanging-process。字符串必须是内建 reporter 名称;第三方 reporter 需 import 类并传入实例。
    outputFilereporter options没有顶层字段。junit / json 用 reporter 元组传 outputPath['junit', { outputPath: '...' }]blob{ outputDir: '...' };其他 reporter 不接受输出路径。对象形 { junit: 'a.xml', json: 'a.json' } 展开为每个 reporter 一条 tuple。
    snapshotFormatsnapshotFormat
    resolveSnapshotPathresolveSnapshotPathRstest 的 callback 签名是 (testPath, snapExtension) => string,没有 Vitest 3+ 的第三个 context 参数。
    snapshotSerializersexpect.addSnapshotSerializer没有配置字段。在 setupFiles 模块里 import 每个 serializer,并调用 expect.addSnapshotSerializer(serializer)
    projectsprojects
    logHeapUsagelogHeapUsage
    detectAsyncLeaksdetectAsyncLeaks用于排查异步资源泄漏。Rstest 会在测试文件结束后报告仍然存活的 Node.js 异步资源。
    includeTaskLocationincludeTaskLocation
    silentsilent与 Vitest 一样支持 boolean | 'passed-only',用于控制被拦截的测试 console 输出。
    printConsoleTraceprintConsoleTrace
    unstubGlobalsunstubGlobals
    unstubEnvsunstubEnvs
    chaiConfigchaiConfig

    文件级环境注释

    如果你的 Vitest 测试使用了文件级环境注释,Rstest 在迁移时会识别 Vitest 环境注释:

    // @vitest-environment jsdom
    // @vitest-environment-options { "url": "https://example.com/" }

    你可以保留这些注释,也可以改名为 @rstest-environment / @rstest-environment-options。选项值必须是单行 JSON 对象。支持的环境包括 nodejsdomhappy-dom

    编译配置

    Rstest 使用 Rsbuild 作为默认测试编译工具,而不是 Vite。你可以在 Build Configurations 查看全部编译配置项。

    Vite 在转换 TypeScript 时会读取 tsconfig.json 中选定的 compiler option。Rstest 会自动使用 compilerOptions.paths 进行模块解析,但不会将其他 compiler option 转为自己的转换配置。迁移时请逐项检查转换设置,并在需要时配置对应的 Rstest 选项,例如用 source.decorators 配置装饰器语法。Vite 的输出 target 配置与 TypeScript compiler option 应分别检查。adapter 可能会继承或推断额外设置,请参考所用 adapter 的文档。详见 source.tsconfigPath。如果你的 Vitest 配置启用了 Vite 的 TypeScript paths 支持,或使用了 vite-tsconfig-paths 插件,请在确认 Rstest 项目使用了预期的 tsconfig.json 后移除这些 Vite 专用配置。

    Rstest 采用基于 bundle 的执行模型来运行测试。没有进入 bundle graph 的代码会保留 Node.js 原生 loader 行为,这可能和 Vitest 不同。如果你在运行时动态加载文件,请参考 bundle graph 之外的代码使用 Node.js 原生行为

    大部分项目中,主要的编译侧变化如下:

    • 使用 source.define 替代 define
    • 使用 output.externals 替代 ssr.external
    • 使用 Rsbuild 插件替代 Vite 插件。
    import { defineConfig } from '@rstest/core';
    - import react from '@vitejs/plugin-react'
    + import { pluginReact } from '@rsbuild/plugin-react';
    
    export default defineConfig({
    -  plugins: [react()],
    -  define: {
    -    __DEV__: true,
    -  },
    +  plugins: [pluginReact()],
    +  source: {
    +    define: {
    +      __DEV__: true,
    +    },
    +  },
    });

    如果你使用的是 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

    - import { describe, expect, it, test, vi, type Mock } from 'vitest';
    + import { describe, expect, it, test, rs, type Mock } from '@rstest/core';
    - vi.fn()
    + rs.fn()
    
    - vi.mock('./foo')
    + rs.mock('./foo')
    
    - vi.spyOn(console, 'error')
    + rs.spyOn(console, 'error')
    - vitest.fn()
    + rs.fn()

    完整工具 API 请参考 Rstest APIs

    全局 API

    当启用 globals: true 时,Vitest 会把 vivitest 挂在全局对象上。在 Rstest 中,建议按以下顺序映射:

    • vi.<api>rs.<api>
    • vitest.<api>rs.<api>

    rsrstest 是等价的全局别名,但统一使用 rs 可以让迁移后的示例和 import style 保持一致。

    - vi.fn()
    + rs.fn()
    
    - vitest.spyOn(console, 'error')
    + rs.spyOn(console, 'error')

    @rstest/core 导入 API 时,统一使用 import style 的 rs.<api> 更一致,避免在同一文件里与 global style 混用。

    Setup adapter

    有些 setup adapter 是 Vitest 专用的。例如 @testing-library/jest-dom/vitest 面向 Vitest;在 Rstest 中通过 expect.extend 直接注册 matcher。

    - import '@testing-library/jest-dom/vitest';
    + import * as jestDomMatchers from '@testing-library/jest-dom/matchers';
    + import { expect } from '@rstest/core';
    +
    + expect.extend(jestDomMatchers);

    路径解析

    new URL('./file', import.meta.url) 会相对于源码模块解析,与 Node.js 和 Vitest 行为一致,因此可以在 setup 或 helper 文件中定位同级文件(资源、fixture):

    const fixtureUrl = new URL('./fixture.json', import.meta.url);

    Rstest 会保留这类表达式,而不会将其改写成打包后的资源路径,因此解析得到的 URL 指向磁盘上的源文件。

    由于这类表达式不会进入打包器的依赖图,在 watch 模式下修改此类同级文件不会自动重跑在运行时读取它的测试。

    自动模拟模块

    Vitest 和 Rstest 都支持在 mock 调用只包含模块路径时自动 mock。Rstest 会先尝试从对应 __mocks__ 目录加载手写 mock;如果没找到,再自动 mock 整个模块,把函数导出替换为空 mock 函数。

    该行为从 Rstest 0.11.0 开始支持。更早版本中,如果没有手写 mock,需要显式传入 { mock: true } 来请求自动 mock。

    // Rstest
    import { rs, test, expect } from '@rstest/core';
    import { someFunction } from './module';
    
    // 优先查找 __mocks__/module.js,然后自动 mock。
    rs.mock('./module');
    
    test('should be mocked', () => {
      expect(rs.isMockFunction(someFunction)).toBe(true);
      someFunction(); // 返回 undefined
    });

    如果你想跳过 __mocks__ 查找并直接请求自动 mock,可以显式传入 { mock: true }

    rs.mock('./module', { mock: true });

    Mock 异步模块

    当你需要 mock 模块返回值时,Rstest 不支持返回异步函数。

    作为替代,Rstest 提供了同步 importActual 能力,你可以通过静态 import 导入未 mock 的真实实现:

    import * as apiActual from './api' with { rstest: 'importActual' };
    
    // 部分 mock './api' 模块
    rs.mock('./api', () => ({
      ...apiActual,
      fetchUser: rs.fn().mockResolvedValue({ id: 'mocked' }),
    }));

    mock factory 是 hoisted 执行的;依赖同模块中后初始化的变量会触发初始化顺序错误。共享值可放到 hoisted initializer(例如 rs.hoisted(...))中规避。

    Snapshot

    Vitest 和 Rstest 使用相同的 snapshot key 格式和 body 序列化方式。原有的 __snapshots__/*.snap 文件可以被 Rstest 原样读取;在 Vitest 下能通过的测试,到 Rstest 下也能通过,不需要重录。两者只有文件 header 行不同:

    - // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
    + // Rstest Snapshot v1

    执行 rstest -u 会把 header 规整为 Rstest 形式,snapshot body 保持 byte-identical。