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/config/test/isolate.md.
close
  • 简体中文
  • isolate

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

    是否运行每个测试在一个独立的环境。

    默认情况下,Rstest 会运行每个测试在一个独立的环境,这会使其避免受到一些模块副作用的影响,从而有助于提升测试的稳定性。

    如果你的代码没有副作用影响,关闭这个选项将有助于提升性能因为可以在不同的测试文件间复用模块缓存。

    Info

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

    模块顶层代码每个 worker 只执行一次

    对于 forks 和 threads,当 isolate 为 false 时,被多个测试文件引入的模块在每个 worker 中只会被求值一次,而非每个文件求值一次。因此该共享模块顶层的所有代码——包括在其模块作用域注册的 hooks(例如某个 helper 在模块作用域调用 beforeEach)——只会对第一个加载它的文件生效,而不会对每个文件生效。如果某段 setup 逻辑需要按文件执行,请使用 setupFiles,即使关闭隔离,Rstest 也会为每个测试文件重新执行它。测试环境遵循同样的规则——它在每个 worker 中只创建一次,其状态(DOM、未清理的 timer、已安装的 fake timers)会跨文件保留,可在 setupFiles 中按文件重置。

    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 中的下一个文件都会重新求值模块。没有使用这种写法的文件仍然共享模块。