Skip to content

vitest-native 0.15 ​

0.15 brings Jest suites over with fewer changes. React Native libraries that ship their own Jest setup or test mock now run with it, as a Jest suite does. Files that need a plugin from your Babel config compile with that config. Snapshots print in Jest's format, and jest-compat applies Jest's manual mocks of installed packages. A new vitest-native compare command runs a suite under Jest and under Vitest and lists every test whose result differs. 0.15 also raises the supported versions: Node 22.22.3+, 24.12+ or 26+, Vitest 5, Vite 8 and React 19.

This page covers what changes for a project on 0.14. The steps to upgrade are in Upgrading to 0.15. Every entry, with the detail behind it, is in the changelog.

Before you upgrade ​

Run npx vitest-native doctor first: it checks your versions and your config.

  • Newer Node, Vitest, Vite and React. Node 22.22.3+, 24.12+ or 26+, Vitest 5 with Vite 8, and React 19. The native engine needs React Native 0.86+ (Expo SDK 57+). An older Node, or Bun under the native engine, stops at startup with UNSUPPORTED_NODE, and an older peer with UNSUPPORTED_PEER. React Native Testing Library 13 and 14 are supported; another version warns. The supported versions table lists each range.
  • Snapshots print in Jest's format. Re-record existing snapshots once with vitest -u, and review the diff. See Re-record snapshots once.
  • Manual mocks of installed packages apply. With jestMockTransform(), a file such as __mocks__/moti.ts replaces that package with no jest.mock() call, as under Jest. jestMockTransform({ rootMocks: false }) keeps the previous behaviour.
  • Testing Library 14 makes render, fireEvent and act async: await render(<App />).
  • Vitest 5 clears mock calls before every test. A suite that asserts on calls recorded before the test started sets test.clearMocks: false.
  • Babel plugins your tests need go through @rolldown/plugin-babel: @vitejs/plugin-react 6 removed its babel option.
  • react-native-gesture-handler/Swipeable and other modules Gesture Handler 3 does not ship no longer resolve, as under Jest.
  • init, doctor and migrate exit 1 on a flag they do not take. Check scripts that pass extra flags.

Compare Jest and Vitest on your suite ​

npx vitest-native compare runs your suite under your own Jest and under Vitest, and names each test that passes under Jest but not under Vitest, grouped by its first error. Use it while both runners are installed, as a migration checklist. Neither run writes snapshots or changes project files. --json <file> writes a report without stack traces or absolute paths, for attaching to an issue. See compare.

vitest-native compare --hot asks a different question: whether hotRuntime: 'auto' changes any test's result in your suite. It lists each test whose result differs between per-file and hot runs. It reports a speed difference only when each mode has at least two timed runs and their ranges don't overlap. See compare --hot.

Your libraries' own Jest support ​

Under the native engine, more libraries run as a Jest suite runs them, with the setup and mocks their authors ship and maintain:

  • Gesture Handler and FlashList run as themselves, with each library's jestSetup.js. Gesture Handler 3's hook API, builder methods and test helpers work, and a FlashList renders a window of rows, as under Jest.
  • Reanimated 4.7+ (with Worklets 0.9+) is Reanimated's own test mock, which has 104 names the hand-written preset lacked, such as interpolateColor, Keyframe and most layout animations. interpolate, interpolateColor and clamp return real values, derived values follow the shared values they read, and shared values have Reanimated 4's shape. Older versions keep the hand-written preset, with the same fixes.
  • The worklets, safe-area-context, keyboard-controller, bottom-sheet and netinfo presets are each library's own Jest mock. Snapshots of the presets' own output may change. The mock engine keeps the hand-written presets.
  • A library's own Jest setup gets a jest with Jest's semantics: its jest.mock reaches every require, jest.requireActual works, and what it installs survives vi.resetAllMocks().

Gesture Handler and FlashList run as themselves and Other libraries' own mocks cover what changes for a suite, and how to keep a preset.

Your Babel config, where a file needs it ​

Files that need a plugin from your project's Babel config now compile with that config, as Metro and babel-jest compile them: legacy decorators (WatermelonDB models), macros, react-native-dotenv, the worklets plugin, custom plugins and Flow. Other files keep Oxc. It needs @babel/core in the project. Remove any babel() entry you added only to run these plugins; doctor names one that repeats a plugin the config already applies. projectBabel: 'never' keeps the previous behaviour.

JSX in your project's own .js files compiles too, so a jest.setup.js that renders a provider, or a .js test, loads without a rename to .jsx.

Closer to Jest ​

  • Snapshots of React Native trees print in Jest's format, and no host prints a ref. vitest-native/serializer keeps the old format for this release; it is deprecated.
  • jest.replaceProperty() works under jest-compat, with Jest's errors and restore behaviour.
  • Under jest-compat, jest.spyOn(View.prototype, 'measure') reaches every View ref, as with React Native's Jest preset.
  • A factory-less jest.mock('pkg') automocks require('pkg') too, with one instance shared by every load in the file.
  • A jest.mock of a react-native/Libraries/... path replaces what react-native returns, including modules with platform files such as BackHandler.
  • A jest.mock factory that requires your own source gets the module the test imports, instead of a second copy.
  • require('@jest/globals') returns jest-compat's shim, as an import does.

Native engine ​

  • A cold run starts at once. React Native's precompiled modules build in a low-priority background process instead of before the run. Test files that start before the build lands load React Native file by file. CI runs and the hot runtime still wait; registryBuild: 'wait' restores the old order.
  • A test file that imports react-native loads only the modules it uses, as under Jest, instead of React Native's whole module graph.
  • A file loaded by both Vite and Node is reported by name, as SOURCE_EVALUATED_TWICE, with one entry per cause, the test files it affects and the fix. ownership: 'error' fails the run on it.
  • Three experimental flags make one module of what Vite and Node load: experimental.ownershipModel: 'resolver' and 'resolution', and experimental.nodeMocks, which lets a vi.mock reach modules Node loads. Nothing changes without them. See experimental.

The hot runtime ​

The hot runtime stays opt-in. In 0.15:

  • A test stuck in synchronous code fails the run, naming the file and test, instead of hanging with nothing on screen. hangTimeout is on by default under the hot runtime.
  • hotRuntime: { freshRuntime: true } (experimental) runs React, its renderers, scheduler and RNTL fresh in every file, so their state no longer carries between files. It costs about 5 ms per file at four workers and about 17 ms at one, and it declines, saying why, while a preset that uses React is active.
  • Fixes for state that carried between files: RNTL's auto-cleanup applies in every file, a partial react-native mock in one file no longer breaks RNTL in later ones, and an ES module dependency sees the running file's preset mock.

CLI ​

  • migrate translates more of a Jest config: local presets, prefix mappers, roots, modulePathIgnorePatterns and root __mocks__. It keeps moduleNameMapper's order, names jest.mock factories that read a module-level const before it is initialised, and lists the Reanimated settings vitest-native covers.
  • init and migrate --write say when the new config takes over from a vite.config.*, and --force replaces the config file Vitest actually loads.
  • init, doctor and migrate refuse a flag they do not take, so a typo such as migrate --wirte no longer runs a dry report silently.
  • doctor warns on Node 24.13.0, which can crash a run at startup, and on Vitest below 5.1.0.

A new home for the docs ​

The documentation moved to vitest-native.danfry.dev. Links in vitest-native's messages, the README and npm point there, and every page of the old GitHub Pages address redirects to the same page here, anchor included. Every error code vitest-native prints has an entry in the new error reference.

How 0.15.0 was built ​

The npm tarball is reproducible from the repository. Packing the v0.15.0 tag from its lockfile gives a tarball whose sha512 equals the one on npm. The release workflow checks this on every release: its publish job packs the tag again and refuses to publish unless the tarball is byte-identical to the one the test jobs ran against. The package is also published with npm provenance, which links it to the workflow run that built it.

To check it yourself:

bash
git clone https://github.com/danfry1/vitest-native.git
cd vitest-native
git checkout v0.15.0
bun install --frozen-lockfile
bun run build
cd packages/vitest-native
npm pack --ignore-scripts
echo "sha512-$(openssl dgst -sha512 -binary vitest-native-0.15.0.tgz | base64)"
npm view vitest-native@0.15.0 dist.integrity

The last two lines print the same value.

Thank you ​

Thank you to @believer for the issues and pull requests in this release.

Released under the MIT License. · llms.txt