> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# Migrating from Jest

Rstest is designed to be Jest-compatible, making migration from Jest projects straightforward. Here's how to migrate your Jest project to Rstest:

## Using Agent Skills

If you are using a Coding Agent that supports Skills, install the [migrate-to-rstest](https://github.com/rstackjs/agent-skills#migrate-to-rstest) skill to help with the upgrade process from Jest to Rstest.

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

After installation, let the Coding Agent guide you through the upgrade.

## Installation and setup

First, you need to install Rstest as a development dependency.


```sh [npm]
npm add @rstest/core -D
```

```sh [yarn]
yarn add @rstest/core -D
```

```sh [pnpm]
pnpm add @rstest/core -D
```

```sh [bun]
bun add @rstest/core -D
```

```sh [deno]
deno add npm:@rstest/core -D
```

Next, update the test script in your `package.json` to use [rstest](/guide/basic/cli.md) instead of `jest`. For example:

```diff
"scripts": {
-  "test": "jest"
+  "test": "rstest"
}
```

### CLI option mappings

Some Jest CLI flags map directly to Rstest, while others move into config. Use this table for the common option differences you are likely to hit during migration:

| Jest CLI option                          | Rstest equivalent                                 | Notes                                                                                                             |
| ---------------------------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `jest`                                   | `rstest`                                          |                                                                                                                   |
| `jest --watch`                           | `rstest --watch` or `rstest watch`                |                                                                                                                   |
| `jest --watchAll`                        | `rstest --watch` or `rstest watch`                | Rstest does not distinguish between `--watch` and `--watchAll`.                                                   |
| `jest --runInBand`                       | `rstest --pool.maxWorkers 1`                      |                                                                                                                   |
| `jest --maxWorkers=50%` or `jest -w 50%` | `rstest --pool.maxWorkers 50%`                    | Rstest's `-w` means `--watch`, not `--maxWorkers`.                                                                |
| `jest --selectProjects app`              | `rstest --project app`                            |                                                                                                                   |
| `jest --env=jsdom`                       | `rstest --testEnvironment jsdom`                  |                                                                                                                   |
| `jest --coverage`                        | `rstest --coverage`                               | Also install the provider package that matches your config: `@rstest/coverage-istanbul` or `@rstest/coverage-v8`. |
| `jest --coverageDirectory=coverage`      | `coverage.reportsDirectory` in `rstest.config.ts` |                                                                                                                   |
| `jest --coverageProvider=v8`             | `coverage.provider: 'v8'`                         | Install `@rstest/coverage-v8` and configure the provider in `rstest.config.ts`.                                   |

## Configuration migration

Update your Jest config file (e.g., `jest.config.js` or `jest.config.ts`) to a `rstest.config.ts` file:

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  // Fill in by mapping fields from your jest.config.js — see the table below.
});
```

### Jest configuration mappings

When migrating, walk through **every** field in your `jest.config.js` and match it against the table below — map it, restructure it, or drop it. Fields not listed here may not map 1:1; verify against the [Rstest config reference](/config.md) before dropping them silently.

| Jest configuration           | Rstest equivalent                                                                                                         | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `preset` (e.g. `'ts-jest'`)  | Remove                                                                                                                    | Rstest uses swc by default; `ts-jest` is not needed.                                                                                                                                                                                                                                                                                                                                                                                    |
| `transform`                  | Remove                                                                                                                    | swc transforms `.ts` / `.tsx` / `.js` / `.jsx` out of the box. For custom Babel, see [Code transformation](#code-transformation).                                                                                                                                                                                                                                                                                                       |
| `testEnvironment`            | [`testEnvironment`](/config/test/test-environment.md)                                                                     |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `testEnvironmentOptions`     | [`testEnvironment.options`](/config/test/test-environment.md)                                                             | Fold into the object form: `testEnvironment: { name: 'jsdom', options: { ... } }`.                                                                                                                                                                                                                                                                                                                                                      |
| `testRegex`                  | [`include`](/config/test/include.md)                                                                                      |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `testMatch`                  | [`include`](/config/test/include.md)                                                                                      | Strip the `<rootDir>/` prefix from each pattern.                                                                                                                                                                                                                                                                                                                                                                                        |
| `testPathIgnorePatterns`     | [`exclude`](/config/test/exclude.md)                                                                                      | Wrap bare `/name/` with `**`, e.g. `'/node_modules/'` → `'**/node_modules/**'`.                                                                                                                                                                                                                                                                                                                                                         |
| `transformIgnorePatterns`    | [`output.bundleDependencies`](/config/build/output.md#outputbundledependencies)                                           | Default differs by `testEnvironment`. For `'node'`: Rstest externalizes `node_modules` — drop most entries; for ESM packages where Jest needed an exception (e.g. `'node_modules/(?!(lodash-es)/)'`), use `output.bundleDependencies: ['lodash-es']` to bundle them through swc. For `'jsdom'` / `'happy-dom'`: `node_modules` is bundled by default — `transformIgnorePatterns` usually has no equivalent and can be dropped entirely. |
| `displayName`                | [`name`](/config/test/name.md)                                                                                            |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `rootDir`                    | [`root`](/config/test/root.md)                                                                                            |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `setupFilesAfterEnv`         | [`setupFiles`](/config/test/setup-files.md)                                                                               | Rstest's `setupFiles` runs after the test framework is registered — equivalent to Jest's `setupFilesAfterEnv` (not Jest's plain `setupFiles`). Strip the `<rootDir>/` prefix. Merge any Jest `setupFiles` entries here too; there is no separate "before framework" hook.                                                                                                                                                               |
| `globalSetup`                | [`globalSetup`](/config/test/global-setup.md)                                                                             | Rstest calls setup with no arguments — if your Jest setup reads `(globalConfig, projectConfig)`, rewrite without them. As in Jest, `process.env` mutations made here propagate to every test worker.                                                                                                                                                                                                                                    |
| `globalTeardown`             | [`globalSetup`](/config/test/global-setup.md)                                                                             | No separate field. Rewrite the file to `globalSetup`'s format (see above) — a bare `export default async function teardown()` from Jest would run at setup, not after tests.                                                                                                                                                                                                                                                            |
| `verbose`                    | [`reporters: 'verbose'`](/config/test/reporters.md)                                                                       | No `verbose` boolean — use the `verbose` reporter.                                                                                                                                                                                                                                                                                                                                                                                      |
| `reporters`                  | [`reporters`](/config/test/reporters.md)                                                                                  | String values must be built-in names. For third-party reporters (e.g. `jest-junit`), import the reporter class and pass the instance.                                                                                                                                                                                                                                                                                                   |
| `injectGlobals`              | [`globals`](/config/test/globals.md)                                                                                      | Jest defaults to `true`; Rstest's `globals` defaults to `false`. If your tests rely on bare `describe` / `test` / `expect` without imports, set `globals: true` and add `@rstest/core/globals` to `compilerOptions.types` in `tsconfig.json`.                                                                                                                                                                                           |
| `moduleNameMapper`           | [`resolve.alias`](/config/build/resolve.md#resolvealias)                                                                  | `resolve.alias` is string-prefix only. Rewrite prefix mappings: `'^@/(.*)$': '<rootDir>/src/$1'` → `resolve: { alias: { '@': './src' } }`. For TypeScript projects, prefer `compilerOptions.paths` in `tsconfig.json` — Rstest reads it automatically. Regex/asset stubs (e.g. `'\\.(css\|svg)$': 'identity-obj-proxy'`) have no direct equivalent; most are unnecessary because Rstest handles CSS/assets natively.                    |
| `maxWorkers`                 | [`pool.maxWorkers`](/config/test/pool.md)                                                                                 |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `testTimeout`                | [`testTimeout`](/config/test/test-timeout.md)                                                                             | For per-test overrides, replace `jest.setTimeout(n)` with `rs.setConfig({ testTimeout: n })`.                                                                                                                                                                                                                                                                                                                                           |
| `slowTestThreshold`          | [`slowTestThreshold`](/config/test/slow-test-threshold.md)                                                                | Jest measures in seconds (default 5); Rstest measures in milliseconds (default 300). Multiply Jest values by 1000.                                                                                                                                                                                                                                                                                                                      |
| `detectOpenHandles`          | No exact equivalent; consider [`detectAsyncLeaks`](/config/test/detect-async-leaks.md) for test-file async leak debugging | Not a 1:1 mapping. Jest's `detectOpenHandles` helps diagnose handles that keep the Jest process from exiting, while Rstest's `detectAsyncLeaks` fails a test file when Node.js async resources remain active after the file finishes.                                                                                                                                                                                                   |
| `fakeTimers`                 | [`rs.useFakeTimers`](/api/runtime-api/rstest/fake-timers.md#rsusefaketimers)                                              | No config field. Call `rs.useFakeTimers(opts)` inside a `setupFiles` module or per-test; pair with `rs.useRealTimers()` to restore.                                                                                                                                                                                                                                                                                                     |
| `bail`                       | [`bail`](/config/test/bail.md)                                                                                            |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `clearMocks`                 | [`clearMocks`](/config/test/clear-mocks.md)                                                                               |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `resetMocks`                 | [`resetMocks`](/config/test/reset-mocks.md)                                                                               |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `restoreMocks`               | [`restoreMocks`](/config/test/restore-mocks.md)                                                                           |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `snapshotFormat`             | [`snapshotFormat`](/config/test/snapshot-format.md)                                                                       |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `snapshotResolver`           | [`resolveSnapshotPath`](/config/test/resolve-snapshot-path.md)                                                            | Rstest takes a function, not a module path.                                                                                                                                                                                                                                                                                                                                                                                             |
| `snapshotSerializers`        | [`expect.addSnapshotSerializer`](/api/runtime-api/test-api/expect.md#expectaddsnapshotserializer)                         | No config field. In a `setupFiles` module, import each serializer and call `expect.addSnapshotSerializer(serializer)`.                                                                                                                                                                                                                                                                                                                  |
| `cacheDirectory`             | Remove                                                                                                                    | Not supported. Rstest manages build caching internally via Rsbuild.                                                                                                                                                                                                                                                                                                                                                                     |
| `collectCoverage`            | [`coverage.enabled`](/config/test/coverage.md#enabled)                                                                    |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `collectCoverageFrom`        | [`coverage.include`](/config/test/coverage.md#include)                                                                    |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `coverageDirectory`          | [`coverage.reportsDirectory`](/config/test/coverage.md#reportsdirectory)                                                  |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `coverageProvider`           | [`coverage.provider`](/config/test/coverage.md#provider)                                                                  | Rstest supports `'istanbul'` (default) and `'v8'`. Use `coverage.provider: 'v8'` for tests that need V8 coverage. `'babel'` should be replaced with `'istanbul'`.                                                                                                                                                                                                                                                                       |
| `coveragePathIgnorePatterns` | [`coverage.exclude`](/config/test/coverage.md#exclude)                                                                    |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `coverageThreshold`          | [`coverage.thresholds`](/config/test/coverage.md#thresholds)                                                              |                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `projects`                   | [`projects`](/config/test/projects.md)                                                                                    | Jest's inline project shape differs — review before porting.                                                                                                                                                                                                                                                                                                                                                                            |

For more details, please refer to the [Configuration](/config.md) section.

### Inject globals

Rstest does not mount the test APIs (e.g., `describe`, `expect`, `it`, `test`) to the global object by default, which is different from Jest.

If you want to continue using the global test APIs, you can enable the `globals` option in your `rstest.config.ts` file:

```ts title='rstest.config.ts'
import { defineConfig } from '@rstest/core';

export default defineConfig({
  globals: true,
});
```

To enable TypeScript to properly recognize the global APIs, add the `@rstest/core/globals` type declaration in your `tsconfig.json`:

```ts title='tsconfig.json'
{
  "compilerOptions": {
    "types": ["@rstest/core/globals"]
  }
}
```

### File-level environment comments

If your Jest tests use file-level environment comments, Rstest recognizes Jest environment comments during migration:

```ts
/**
 * @jest-environment jsdom
 * @jest-environment-options { "url": "https://example.com/" }
 */
```

You can keep these comments as-is or rename them to `@rstest-environment` / `@rstest-environment-options`. The options value must be a single-line JSON object. Supported environments are `node`, `jsdom`, and `happy-dom`.

### Code transformation

Rstest uses `swc` for code transformation by default, which is different from Jest's `babel-jest`. Most of the time, you don't need to change anything. And you can configure your swc options through [tools.swc](/config/build/tools.md#toolsswc).

Jest itself gets module resolution and transformation from `moduleNameMapper` and `transform`, rather than from `tsconfig.json`. If you use `ts-jest`, do not assume that its TypeScript transform settings carry over: Rstest automatically applies `compilerOptions.paths` for resolution, while transformation settings, such as decorator syntax or output-target requirements, should be reviewed and configured through Rstest when needed. An adapter can inherit or infer additional settings, so refer to the documentation for the [adapter you use](/guide/advanced/adapters.md). See [source.tsconfigPath](/config/build/source.md#sourcetsconfigpath).

Rstest transforms files that are part of its bundle graph. If your project previously relied on `ts-jest` runtime transforms, see [Code outside the bundle graph uses native Node.js behavior](/guide/debug/troubleshooting.md#code-outside-the-bundle-graph-uses-native-nodejs-behavior).

```diff
export default {
-  transform: {
-    '^.+\\.(t|j)sx?$': ['@swc/jest', {}],
-  },
+  tools: {
+    swc: {}
+  }
}
```

However, if you have custom Babel configurations or use specific Babel plugins/presets, you can add [Rsbuild's Babel Plugin](https://rsbuild.rs/plugins/list/plugin-babel):

```ts title='rstest.config.ts'
import { pluginBabel } from '@rsbuild/plugin-babel';
import { defineConfig } from '@rstest/core';

export default defineConfig({
  plugins: [pluginBabel()],
});
```

## Environment variables

Rstest mirrors Jest's two main injected variables under different names:

| Jest             | Rstest             | Notes                                                                                                                                                                                                                             |
| ---------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `JEST_WORKER_ID` | `RSTEST_WORKER_ID` | Same semantics — a stringified integer, unique among workers active at the same time. Use it to isolate per-worker DBs / ports / temp dirs. See [`RSTEST_WORKER_ID`](/api/runtime-api/environment-variables.md#rstest_worker_id). |
| `NODE_ENV`       | `NODE_ENV`         | Both default to `'test'` when not set.                                                                                                                                                                                            |

Replace any references in tests, fixtures, or `setupFiles`:

```diff
- const dbName = `myapp_test_${process.env.JEST_WORKER_ID}`;
+ const dbName = `myapp_test_${process.env.RSTEST_WORKER_ID}`;
```

See [Environment variables](/api/runtime-api/environment-variables.md) for the full list.

## Update test API

### Test API

Your existing Jest test files should work with minimal changes since Rstest provides Jest-compatible APIs. Simply update your imports from Jest to Rstest:

```diff
- import { describe, expect, it, test } from '@jest/globals';
+ import { describe, expect, it, test, rs } from '@rstest/core';
```

Rstest provides an `rs` API that you can use to access Rstest's utilities, such as `rs.fn()` and `rs.mock()`. The longer `rstest` helper remains available as an alias. More utilities can be found in the [Rstest APIs](/api/runtime-api/index.md).

```diff
- const fn = jest.fn();
+ const fn = rs.fn();

fn.mockResolvedValue('foo');
```

### Virtual module mocks

When migrating a Jest virtual mock, first [declare the module and configure `resolve.alias`](/api/runtime-api/rstest/mock-modules.md#mock-virtual-modules). This setup works in both Node and Browser mode. Then remove Jest's third argument:

```diff
- jest.mock('native-runtime', () => ({ platform: 'test' }), {
-   virtual: true,
- });
+ rs.mock('native-runtime', () => ({ platform: 'test' }));
```

The `rs.mock` factory or a matching manual mock provides the module's runtime exports.

### Done callback

The `done` callback is not supported in Rstest. Instead, you can return a Promise or use `async/await` for asynchronous tests.

```diff
- test('async test with done', (done) => {
+ test('async test with done', () => new Promise(done => {
  // ...
  done();
- });
+ }));
```

If you need to handle errors, you can modify it as follows:

```diff
- test('async test with done', (done) => {
+ test('async test with done', () => new Promise((resolve, reject) => {
+   const done = err => (err ? reject(err) : resolve());
  // ...
  done(error);
- });
+ }));
```

### Hooks

The return functions of the `beforeEach` and `beforeAll` hooks in Rstest are used to perform cleaning work after testing.

```diff
- beforeEach(() => doSomething());
+ beforeEach(() => { doSomething() });
```

### Timeout

If you used `jest.setTimeout()` to set the timeout for a test, you can use `rs.setConfig()` instead.

```diff
- jest.setTimeout(5_000)
+ rs.setConfig({ testTimeout: 5_000 })
```

## Snapshot format

Rstest snapshots use a different key format than Jest. Existing Jest snapshot files will not match Rstest's generated keys on the first run — the snapshot bodies are unchanged, only the key formatting differs.

### Key separator change

Jest joins the suite name, test name, and snapshot label with `:`. Rstest uses `>`:

```diff
- overlay should not show a warning when "client.overlay.warnings" is "false": page html 1
+ overlay > should not show a warning when "client.overlay.warnings" is "false" > page html 1
```

For the example above, the parts are:

1. `overlay` — suite name (`describe(...)`).
2. `should not show a warning when "client.overlay.warnings" is "false"` — test name (`it(...)` / `test(...)`).
3. `page html` — snapshot label from `.toMatchSnapshot('page html')`.
4. `1` — snapshot index for that label in the test.

### Updating snapshots

`rstest -u` re-records snapshots in Rstest's key format:

```bash
rstest -u                # update everything
rstest overlay -u        # update a filtered scope
```

### Reviewing the diff

Most snapshot churn after migration is non-functional:

- Separator-only key renames are formatting changes, not behavior changes.
- Key-order churn without any body difference is also formatting-only.
- Body content changes under the same key are the only signal of a real behavior change.

## ESM and CJS

Rstest supports ESM by default. If your project is using ESM, you don't need to perform any extra configuration, such as setting `NODE_OPTIONS=--experimental-vm-modules`.

If your project is using CommonJS, Rstest still works, but it is recommended to migrate to ESM for better performance and future compatibility.

### ESM vs CJS mocking

In Rstest, `rs.mock()` targets ESM entries used by `import`, while `rs.mockRequire()` targets CJS entries used by `require()`.

For code that uses `require()`, `rs.mockRequire()` targets the CJS entry:

```ts
// Mocking a CJS module
rs.mockRequire('./math.cjs', () => ({
  sum: (a, b) => a + b + 100,
}));
```
