For AI agents: the complete documentation index is available at https://rstest.rs/zh/llms.txt, the full documentation bundle is available at https://rstest.rs/zh/llms-full.txt, and this page is available as Markdown at https://rstest.rs/zh/config/test/isolate.md.
close
  • 简体中文
  • isolate

    • 类型: boolean
    • 默认值: true
    • CLI: --isolate, --isolate=false, --no-isolate

    控制测试文件之间的隔离。默认情况下,每个文件有独立的模块状态和测试环境,避免一个文件的状态影响另一个文件。

    同一文件中的测试仍然共享该文件的环境。需要在单个测试之间重置状态时,请使用 beforeEach 和 afterEach。

    对于 forks 和 threads,设置 isolate: false 后,同一 worker 中的文件可以复用模块缓存和测试环境。如果文件不会留下影响后续测试的状态,这样可以提升性能。

    隔离不控制并行数量。限制 Node.js 测试 worker 数量请使用 pool.maxWorkers,限制文件内并发测试用例数量请使用 maxConcurrency。

    Info

    vmForks 和 vmThreads 始终按文件隔离 当 pool.type 为 vmForks 或 vmThreads 时,此选项不生效。Rstest 会复用 worker,但会为每个文件创建新的 VM context、module graph 和 test environment。因此,setup 文件和 worker scope fixture 也仍保持文件级生命周期。

    关闭隔离后的共享状态

    对于 forks 和 threads,设置 isolate: false 后:

    • 多个文件引入同一模块时,该模块每个 worker 只执行一次。因此,在共用 helper 顶层注册的 hook 只会对第一个引入它的文件生效。
    • setupFiles 仍会在每个测试文件执行前运行。共用 hook 的注册和按文件重置状态的逻辑应放在这里。
    • 同一 worker 会复用测试环境。DOM 状态、未清理的 timer 和已安装的 fake timers 可能跨文件保留;后续文件需要干净的环境时,必须显式重置这些状态。
    CLI
    rstest.config.ts
    npx rstest --no-isolate

    模块 mock

    由于模块在同一个 worker 的多个文件之间共享,当 isolate 为 false 时,模块 mock 的行为会有所不同:

    • mock 可能不生效:如果之前的文件已经加载了某个引入被 mock 模块的模块,这个已缓存的模块会继续使用真实实现。
    • mock 可能泄漏:mock 生效期间加载的模块,会在后续文件中继续使用 mock 实现。

    具体哪个文件失败,取决于文件的执行顺序。

    例如,下面两个测试文件都引入了 ./user-service,而它会调用 ./api 中的 fetchUser()。真实的 fetchUser() 返回 { name: 'Bob' },只有 user-service.test.ts 对它进行了 mock。为了让两个文件在任意执行顺序下都正确,mock 文件在引入被测代码之前重置模块缓存,并在文件结束后撤销 mock(高亮行):

    user-service.test.ts
    import { afterAll, expect, rs, test } from '@rstest/core';
    import { loadUserName } from './user-service';
    
    rs.hoisted(() => {
      rs.resetModules();
    });
    
    rs.mock('./api', () => ({ fetchUser: () => ({ name: 'Alice' }) }));
    
    afterAll(() => {
      rs.doUnmock('./api');
      rs.resetModules();
    });
    
    test('loads the mocked user name', () => {
      expect(loadUserName()).toBe('Alice');
    });
    profile.test.ts
    import { expect, test } from '@rstest/core';
    import { loadUserName } from './user-service';
    
    test('loads the real user name', () => {
      expect(loadUserName()).toBe('Bob');
    });

    profile.test.ts 无需任何修改:无论它在 user-service.test.ts 之前还是之后执行,拿到的都是真实的 ./api。

    • 在 rs.hoisted() 中调用 rs.resetModules(),让模块缓存在静态 import 执行之前被清空。
    • 在 afterAll 中对该文件 mock 的每个模块调用 rs.doUnmock()。
    • 两次 rs.resetModules() 都会清空共享的模块缓存,因此当前文件和同一 worker 中的下一个文件都会重新求值模块。没有使用这种写法的文件仍然共享模块。