skip to content

Unit Test Setup

Running React Native code under Jest in Node: the react-native preset, mocks for native modules that don't exist off-device, and which test layer catches what. Interviewers probe the setup pain.

part ofReact Nativeoverview, primer and where to startread it →
on this pageshow

explore

questions

13

In a React Native 0.87 app, what does @react-native/jest-preset configure, and why can't plain Jest run React Native code?

level: juniorimportance: must knowfreq 55%

answer

  1. Flow, JSX and TypeScript need compiling
  2. node_modules allowlist for react-native
  3. image requires become testUri stubs
  4. setup file: __DEV__ plus core mocks
  5. separate package since 0.85

basics

~10 s

@react-native/jest-preset makes Jest able to load React Native code: it compiles JS and TS through babel-jest, transforms React Native's own untranspiled packages, stubs image requires, uses a Node-based environment and mocks core native modules.

solid answer

~40 s

React Native ships its JavaScript as untranspiled source (Flow types, JSX, ES modules) and expects native modules that only exist on a device, so plain Jest either fails to parse it or crashes calling native code. The preset, a separate package `@react-native/jest-preset` since 0.85, fixes both: `transform` runs `.js`, `.ts` and `.tsx` through `babel-jest` and image files through a stub transformer; `transformIgnorePatterns` lets `react-native` and `@react-native/*` packages be compiled; `setupFiles` loads a script that defines `__DEV__`, `requestAnimationFrame` and friends and mocks core modules such as `Linking`, `AppState` and `NativeModules`; `testEnvironment` is its Node-based `react-native-env`. In 0.87 you install it and set `preset: '@react-native/jest-preset'`.

code

javascript · 4 lines
javascript
// jest.config.js
module.exports = {
  preset: '@react-native/jest-preset',
};

go deeper

for a junior

Recall that React Native code needs compiling and native mocks before Jest can run it, and that the preset is the package @react-native/jest-preset set in jest.config.js.

for a middle

Walk through the preset's keys: babel-jest transform, the node_modules allowlist in transformIgnorePatterns, the setup file with globals and core mocks, and the Node-based react-native-env.

for a senior

Use the preset's boundaries to triage failures: parse errors mean an untranspiled dependency, crashes in native calls mean a missing mock, device-only behaviour means the test is at the wrong layer.

for a principal

Treat the preset as a pinned dependency that moves with each React Native upgrade, and decide how much native behaviour a team may trust from Node-side tests before paying for device runs.

## Why plain Jest cannot run React Native code Jest is a test runner for JavaScript that executes in **Node.js**. A React Native app is written for a very different host: Metro bundles it, Hermes runs it on the phone, and the framework's components talk to native iOS and Android code. Two things break when you point an unconfigured Jest at a React Native project: 1. **Syntax.** The `react-native` package and many `@react-native/*` packages are published as source that still contains Flow type annotations, JSX and `import`/`export` statements. Node's module loader inside Jest cannot parse that, and Jest by default does **not** transform anything under `node_modules`. 2. **Native dependencies.** Core modules such as `Linking`, `AppState`, `Vibration` or the view manager expect a native implementation. In Node there is no device, so calling them without a mock throws. The **Jest preset** is a ready-made bundle of Jest configuration that solves both problems so that a component test can simply `import { View } from 'react-native'`. ## What the preset configures The preset is published as **`@react-native/jest-preset`**. React Native 0.85 moved it out of the `react-native` package, and the 0.87 release notes say it must now be consumed as that package, so `preset: 'react-native'` no longer resolves. Its `jest-preset.js` sets these keys: | Key | Value in the preset | Why it matters | |---|---|---| | `transform` | `.js`, `.ts`, `.tsx` through `babel-jest`; image and video files through an asset transformer | Your code and React Native's source get compiled with your Babel config | | `transformIgnorePatterns` | `node_modules/(?!((jest-)?react-native\|@react-native(-community)?)/)` | Everything in `node_modules` is skipped **except** `react-native`, `jest-react-native` and the `@react-native/` and `@react-native-community/` scopes | | `setupFiles` | the preset's `jest/setup.js` | Globals and core mocks exist before any test file loads | | `testEnvironment` | `jest/react-native-env.js` | A Node environment with React Native export conditions, not a browser DOM | | `haste` | `defaultPlatform: 'ios'`, platforms `android`, `ios`, `native` | Platform-specific files such as `Button.ios.tsx` resolve in tests | | `moduleNameMapper` and `resolver` | map `react-native` subpaths to the installed package | Deep paths such as the `Libraries/...` modules can still be resolved and mocked | ## What the setup file does The file listed in `setupFiles` runs once per test file, before the test code. It: - sets `__DEV__` to `true` and `IS_REACT_ACT_ENVIRONMENT` to `true`; - defines `requestAnimationFrame` on top of `setTimeout` and `performance.now` as a Jest mock of `Date.now`; - makes `window` an alias of the Node global object; - registers mocks for core modules and components, among them `AppState`, `AccessibilityInfo`, `Clipboard`, `Image`, `Linking`, `Modal`, `ScrollView`, `Text`, `TextInput`, `View`, `UIManager`, `NativeModules` and `Vibration`. The asset transformer turns `require('./logo.png')` into an object with a `testUri` field holding the file's relative path, so snapshots show which image a component asked for instead of failing on binary data. ## Installing it in React Native 0.87 1. Install the package as a dev dependency at the same version as `react-native` (the package is versioned in lockstep, for example 0.87.1). 2. Point Jest at it in `jest.config.js` with `preset: '@react-native/jest-preset'`. 3. Keep a `babel.config.js` that uses `@react-native/babel-preset`, because `babel-jest` reads the project's Babel configuration. 4. Add your own options on top: extra `setupFiles`, a widened `transformIgnorePatterns`, `setupFilesAfterEnv` for matcher libraries. Projects created with Expo usually use the `jest-expo` preset instead, which Expo's docs describe as mocking the native part of the Expo SDK and handling most of the configuration an Expo project needs. ## What the preset does not do - It does **not** run your code on a device or simulator: rendering happens in Node, so real layout, gestures and native behaviour are not exercised. - Its native mocks cover **core** React Native modules. Third-party native libraries bring their own Jest mocks or need one written for them. - It does **not** type-check TypeScript: Babel strips types and moves on. - It does not choose a component testing library; React Native Testing Library is added separately. Knowing this boundary is what lets you explain a failing test: a syntax error in a dependency points at `transformIgnorePatterns`, a crash inside a native call points at a missing mock, and a behaviour that only shows on a phone belongs to a device-level test.

  • In a React Native project with Button.ios.tsx and Button.android.tsx, which file does a Jest test import under the preset?
    The preset's `haste` block sets `defaultPlatform: 'ios'` with the platforms `android`, `ios` and `native`, so an import of `./Button` resolves `Button.ios.tsx`. Testing the Android variant needs its own configuration, for example a second Jest project with a different default platform, or importing the Android file directly.
  • Does an Expo project use @react-native/jest-preset directly?
    Usually not. Expo documents the `jest-expo` preset, which builds a Jest configuration for Expo projects and mocks the native part of the Expo SDK. You still widen `transformIgnorePatterns` the same way when a dependency ships untranspiled code.
  • Why does the preset map image requires to an object with testUri instead of a number?
    In a Metro bundle an image `require` returns an asset registry id, but Jest has no asset registry. The preset's asset transformer replaces the file with `{ testUri: '<relative path>' }`, so the component still receives a value and snapshots record which image was requested.

The preset is a travel adapter kit for running phone code on a laptop: a plug converter for the syntax (Babel), a list of which bags get unpacked (the node_modules allowlist) and stand-in appliances for the sockets that do not exist here (core native mocks).

saying these in an interview costs you the question

  • The preset runs the tests on an iOS simulator or Android emulator
  • The preset mocks every third-party native module automatically
  • In React Native 0.87 you still write preset: 'react-native'
  • babel-jest in the preset type-checks the TypeScript tests
  • The preset renders components into a jsdom document
open as a page

In a React Native app's Jest suite, why does a calendar's untranspiled date library fail on import statements, and how do you fix it?

level: middleimportance: must knowfreq 60%

basics

~10 s

Jest skips transforming files that match transformIgnorePatterns, and the React Native preset only allowlists react-native and @react-native packages, so an ESM date library reaches Node untranspiled. Add the package to the same negative-lookahead regex.

open as a page

What can a React Native component test under Jest and React Native Testing Library not see that a device test can?

level: middleimportance: must knowfreq 58%

basics

~20 s

A Jest and React Native Testing Library test runs only JavaScript in Node: native modules are mocks, no layout is computed, gestures are simulated by calling handlers, and the engine and build differ from the shipped app.

open as a page

In React Native Jest tests, why does a screen using a custom location TurboModule fail on import, and how do you mock it?

level: middleimportance: must knowfreq 55%

basics

~20 s

Under Jest there is no native binary, so TurboModuleRegistry.getEnforcing cannot find the location module and throws when the spec file is imported. Mock that spec module (or your wrapper around it) with jest.mock and a factory of jest.fn methods.

open as a page

In React Native Jest tests, what is Platform.OS by default, and how do you test a component's Android branch?

level: juniorimportance: should knowfreq 38%

basics

~20 s

Platform.OS is 'ios' in tests because the React Native preset's haste defaultPlatform is 'ios', so Platform.ios.js and .ios files load. Test Android branches with a second Jest project whose defaultPlatform is 'android', or by injecting the value.

open as a page

In a React Native TypeScript project using @react-native/jest-preset, how are .tsx tests compiled, and why don't type errors fail the run?

level: middleimportance: should knowfreq 35%

basics

~10 s

The preset sends .js, .ts and .tsx files to babel-jest, which applies the project's Babel config with @react-native/babel-preset; its TypeScript plugin strips types without checking them, so type errors need a separate tsc run.

open as a page

For React Native end-to-end tests, how do Detox, Maestro and Appium differ in how they drive the app and wait for it?

level: middleimportance: should knowfreq 46%

basics

~20 s

Detox is gray-box: a native client inside the app build lets it wait until React Native, network and animations are idle. Maestro and Appium drive the app black-box from outside, Maestro from YAML flows and Appium through the WebDriver protocol.

open as a page

In a React Native Jest suite, how do you wire the mocks that AsyncStorage, NetInfo and Reanimated ship instead of writing your own?

level: middleimportance: should knowfreq 42%

basics

~10 s

Each library ships its own Jest integration: AsyncStorage's in-memory mock at @react-native-async-storage/async-storage/jest, NetInfo's jest/netinfo-mock.js, and Reanimated's Jest resolver plus setUpTests() in a setup file.

open as a page

After upgrading a React Native app from 0.84 to 0.87, Jest cannot find the react-native preset; what changed, and what else must you recheck in the config?

level: seniorimportance: should knowfreq 28%

basics

~10 s

React Native 0.85 moved the Jest preset into @react-native/jest-preset and 0.87 requires consuming it from there. Install it at the matching version, change preset, and fix custom config that still points at react-native/jest paths.

open as a page

For a React Native ticket-resale app's checkout flow, which checks go in Jest, RNTL and device E2E tests, and how do you keep each layer stable?

level: seniorimportance: should knowfreq 36%

basics

~20 s

Put fee maths and seat-hold rules in Jest, each checkout screen state in RNTL against a mocked API, and one or two purchase paths plus native pieces such as the payment sheet in device E2E against a stubbed backend.

open as a page

A React Native Jest suite with Animated transitions flakes in CI and logs act() warnings after tests end; what causes it, and how do fake timers fix it?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Animated animations keep scheduling frames on real timers after a test finishes, so callbacks and state updates land late and timing depends on CI speed. Fake timers make the animation advance only when the test calls jest.advanceTimersByTime.

open as a page

How would you split a React Native team's test budget across Jest, component tests and device E2E, and what would make you change the split?

level: principalimportance: should knowfreq 24%

basics

~20 s

Spend most tests on fast Jest and component tests, keep device E2E to vital flows and native behaviour, and move the line when escaped bugs, flake rates, native churn or release channels show the current split is protecting the wrong things.

open as a page

Why does React Native's Jest preset run tests in its Node-based react-native-env instead of jsdom, and what does that change?

level: middleimportance: nice to knowfreq 22%

basics

~20 s

React Native renders native views, not DOM nodes, so a browser DOM would give tests APIs the app never has. The preset's react-native-env extends Jest's Node environment and resolves packages with the react-native export condition.

open as a page