skip to content

Coverage Layer Choice

A React Native app splits tests across pure logic under Jest, components under RNTL, and flows on a device with Detox, Maestro or Appium. Interviewers ask what each layer misses and costs.

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

explore

questions

4

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%

answer

  1. JavaScript only, no platform code
  2. mocked native modules
  3. no Yoga layout, no hit-testing
  4. handlers called, gestures not recognised
  5. Node engine and a debug-like build

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.

solid answer

~40 s

Component tests render to a JavaScript tree in Node, so they check your React logic and what it renders, not the phone. They cannot see **native code**, because every native module is a mock; **real layout**, because Yoga never runs, so clipped text, a keyboard covering an input or a button pushed off-screen go unnoticed; **real gestures**, because `fireEvent` or `userEvent` call the handler props without native hit-testing, scroll physics or gesture recognisers; and the **runtime and build**, because tests use Node's engine with `__DEV__` true, not Hermes in a release build. OS integration such as permission dialogs, deep links on cold start, push and backgrounding is also out of reach. Those gaps are what device-level tests with Detox, Maestro or Appium exist for.

go deeper

for a junior

Recall that React Native component tests run JavaScript in Node with mocked native modules, so they cannot show how the app behaves on a phone.

for a middle

Name the concrete gaps: native modules, layout, hit-testing, gestures, engine and build, OS integration, and which test layer covers each.

for a senior

Recognise bugs that pass every component test, such as drifting native contracts or keyboard-covered buttons, and add targeted device checks for them.

for a principal

Map each blind spot to the product risk it hides, so device-level spending follows where real users would be hurt.

## What a component test actually runs A React Native component test combines **Jest**, the runner, with **React Native Testing Library** (RNTL), which renders components through a test renderer into plain JavaScript objects in **Node.js**. React Native's own documentation is explicit: component tests are JavaScript tests running in Node and do not take into account the iOS or Android code backing the components, so a bug in that code will not be found. RNTL's documentation lists the same limits: tests don't execute native code, are unaware of view state managed by native components, don't assert on the native view hierarchy, and simulate runtime behaviour, sometimes imperfectly. That makes them excellent at one thing: checking that **your JavaScript** renders the right elements and reacts correctly to events and data. Everything below that line is invisible. ## The blind spots, grouped | Blind spot | Why Node cannot see it | Typical bug it hides | |---|---|---| | **Native modules** | Every native call hits a Jest mock that returns what the test says | A module registered under a different name, or returning a different shape, on one platform | | **Layout** | Yoga, the layout engine, never runs, so no sizes or positions exist | Text truncated by `numberOfLines`, content under the notch, a button hidden by the keyboard | | **Hit-testing** | Without layout there is no way to know what is on top | A pressable covered by an overlay still "presses" in the test | | **Gestures and scrolling** | Events are dispatched to handler props directly | A swipe that conflicts with a scroll view, momentum scrolling, gesture-handler recognisers | | **Native UI components** | Core components such as `Modal`, `ScrollView` and `TextInput` are mocks | The Android back button dismissing a modal, native text input behaviour | | **Engine and build** | Node's JavaScript engine, `__DEV__` set to true, no release minification | A Hermes-specific difference, a branch that only runs in release | | **OS integration** | No operating system is present | Permission prompts, deep links on cold start, notifications, app backgrounding | | **Performance** | No frames are rendered | Dropped frames in a long list, slow startup | ## Why "the test passed" can still mean "the app is broken" Three patterns come up in interviews: 1. **The mocked contract drifts.** A native module's method is renamed on Android. The Jest mock still exposes the old name, so every component test passes while the Android app crashes on the call. 2. **Layout-dependent behaviour.** A checkout button sits below the fold on small phones and is covered by the keyboard when the promo-code field is focused. RNTL finds and presses it without complaint. 3. **Build-dependent behaviour.** Code guarded by `__DEV__` behaves differently in release, and release builds apply minification; the preset's test environment runs with `__DEV__` true. ## What each layer is for - **Pure Jest tests** cover functions and hooks with no UI: formatting, validation, reducers, pricing rules. They are the fastest and least flaky. - **Component tests** with RNTL cover a screen's JavaScript behaviour: what renders for each state, which handler runs on press, which error text appears when a mocked request fails. - **Device tests** with Detox, Maestro or Appium run a real build on a simulator, emulator or device and cover what only the platform can show: native modules, layout, gestures, navigation transitions and OS integration. React Native's documentation recommends covering the vital parts of an app, such as authentication, core functionality and payments, with end-to-end tests and using faster JavaScript tests for the rest, because device tests take longer to write, run slower and are more prone to flakiness. ## How to answer in an interview State the model first: component tests run JavaScript in Node against mocks, so they prove the JavaScript contract and nothing native. Then name the concrete gaps (native modules, layout, gestures, engine and build, OS integration) and close with the consequence: each gap you care about needs a device-level check, and you choose those deliberately because they cost more than component tests.

  • RNTL's toBeVisible passes for a React Native button that users report they cannot see on small phones; why?
    Visibility in a component test is judged from props and styles such as `display: 'none'` or zero opacity, because no layout is computed. A button pushed below the fold or behind the keyboard still counts as visible. Only a device test, where the platform lays out the screen, can catch it.
  • If component tests miss native bugs, why not write every React Native test as a device test?
    Device tests need a native build and a booted simulator or emulator, run far slower, and flake more because of timing, network and device state. Covering every branch that way would make feedback slow and noisy. Keep device tests for vital flows and native behaviour, and cover branches in Jest and RNTL.

A component test is a flight simulator: it checks that the pilot presses the right controls in the right order, but the wind, the runway and the engines are all simulated. Only a real flight shows how the aircraft behaves.

saying these in an interview costs you the question

  • RNTL renders into a real native view hierarchy in the background
  • A component test proves native modules behave the same on iOS and Android
  • fireEvent.press goes through native hit-testing like a real tap
  • Component tests run on Hermes, so engine differences are covered
  • Layout problems such as clipped text show up in RNTL snapshots
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

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

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