Announcing Rstest 0.11
July 6, 2026
Rstest 0.11 is a transitional release on the road to 1.0. Its focus is to settle several public APIs through breaking changes ahead of the stable release; alongside that, it also brings faster V8 coverage and updated fake timers.
Notable changes:
- API changes on the road to 1.0
- Faster V8 coverage
- Fake timers aligned with
@sinonjs/fake-timers15 - Other improvements
API changes on the road to 1.0
To stabilize the public API before 1.0, Rstest 0.11 adjusts a few surfaces. These are breaking changes, but the migrations are straightforward.
TestOptions moves to the second argument
The TestOptions object (retry / repeats / timeout) for test / it / test.each / test.for was previously accepted as the last argument, after the test function. With a long test body it was easy to miss, and formatters tended to push it onto a separate trailing line. It now goes in the second position, before the test function, matching the shape used by Vitest and Node.js' built-in test runner (node:test).
The numeric timeout shorthand is unchanged — the Jest-style test('name', fn, 1000) still works as the last argument. describe now accepts a TestOptions object in the same second-argument position.
Mock factories are synchronous-only
The rs.mock / rs.doMock factory type is now synchronous-only. Async factories are prone to hoisting and module-initialization issues, so TypeScript no longer suggests them. For partial mocks, keep the real module via a synchronous importActual import attribute and override only the exports you need:
See Partially mock modules for more details.
pool.minWorkers is removed
pool.minWorkers set a floor on retained idle workers, but it only applied under isolate: false — the default isolate: true never reuses workers, so the option had no effect. It is removed so that pool settles on the flat { type?, maxWorkers?, execArgv? } shape; use pool.maxWorkers to control file-level parallelism. The internal floor still defaults to min(maxWorkers, recommended), so default warm-worker behavior is unchanged.
shard is CLI-only
Sharding is inherently per-invocation — every CI runner passes a different index, which a value committed to a shared config file cannot express. The shard config field is removed from RstestConfig; use the --shard <index>/<count> CLI flag instead. The flag and its behavior are unchanged.
Faster V8 coverage
On a large bundled project, collecting V8 coverage could spend tens of seconds turning raw V8 data into Istanbul coverage. Rstest 0.11 reworks the conversion path.
Previously, the provider ran every raw V8 entry through the generic ast-v8-to-istanbul converter, including many that never reach the final report. Rstest 0.11 uses a converter tailored for Rstest's output, filters out irrelevant entries before converting, and moves the conversion from each worker into the main process.
Measured on the Rsbuild repository's own test suite (pnpm test --coverage --coverage.provider=v8):
Compared with the baseline, the reworked path is about 3.34x faster, and the summed per-file phase time drops 5.85x. Resolving raw coverage in the main process further cuts Rstest's own processing time (about 2x on the same project) and lowers CPU usage. The coverage output is unchanged.
There is no configuration change — projects using coverage.provider: 'v8' get the speedup automatically.
Fake timers aligned with @sinonjs/fake-timers 15
Rstest 0.11 upgrades to @sinonjs/[email protected] and aligns the fake timers runtime with its API. Two methods are now available:
rstest.jumpTimersByTime(ms)jumps the clock forward bymswithout running the timers in between, so any timer scheduled within that span fires at most once, at the destination.rstest.setTickMode({ mode })switches the clock between manual and auto-advancing modes.
rstest.setSystemTime(date) also works on its own now — without a prior rstest.useFakeTimers() call it pins only the global Date and leaves the real timers intact, so you can freeze the system clock without taking over setTimeout / setInterval.
This release also closes a Browser Mode gap. Browser Mode previously aliased @sinonjs/fake-timers to a minimal stub that kept a clock-shaped object but did not actually fake browser globals such as setTimeout or Date. Rstest 0.11 removes that stub and bundles the real implementation, so fake timers behave consistently across Node mode and Browser Mode — including Date / performance, animation frames, microtasks, and string durations. See Fake Timers for more details.
Other improvements
- Clearer error for
rs.spyOnon ESM namespace exports. Previously, spying on a read-only ESM namespace export produced an opaque failure; it now produces an actionable error explaining why the binding cannot be reassigned. new URL()and Wasm resolve from on-disk source. Assets referenced vianew URL(..., import.meta.url)and Wasm modules now resolve from the original source location, matching runtime behavior.- Shared module state under
isolate: false. Imported module state is now shared and context-bound APIs stay live across files whenisolate: false, so cross-file singletons behave as expected. - Browser Mode project isolation. Each project now runs in its own Rsbuild instance, fixing cross-project module resolution in multi-project browser runs.
- OS-native
testPath.testPathis now surfaced with the platform's native path separators. - Custom
runCLIargv. Programmatic callers can pass a customargvarray torunCLI, whileprocess.argvremains the default.
Upgrade
Upgrade the @rstest/* packages to version 0.11 to get these improvements. Rstest 0.11 includes a few breaking API changes — see API changes on the road to 1.0 for the migration steps.
For a full list of changes, see the v0.11.0 release notes.