skip to content

In Detox, when should a test call device.launchApp({ newInstance: true }), device.reloadReactNative() or device.openURL() in a sign-up suite?

level: middleimportance: should knowfreq 35%

answer

  1. newInstance defaults to false
  2. reload resets JS only
  3. reload is not fault-free
  4. openURL hits the running app
  5. launchApp({ url }) for cold start

basics

~20 s

device.launchApp({ newInstance: true }) restarts the app process; device.reloadReactNative() only reloads the JS bundle, fast but leaving native state and sometimes misbehaving; device.openURL() delivers a link to the running app, while launchApp({ url }) tests a link at launch.

solid answer

~40 s

`device.launchApp()` resumes a running app, because `newInstance` defaults to `false`; `newInstance: true` kills and relaunches the process, and `delete` or `resetAppState` also wipe data. `reloadReactNative()` reloads only the JS bundle, so navigation and React state reset quickly while native state and stored data survive — and the docs warn it isn't fault-free, with `launchApp` as the fallback. `openURL({ url })` delivers a URL to the running app, as tapping a link elsewhere would; for the cold-start path I use `launchApp({ url, newInstance: true })`. In a meal-kit sign-up suite I launch once with notification permission granted, reload between simple tests, and relaunch when the flow stores native state.

code

typescript · 9 lines
typescript
import { by, device, element, expect } from 'detox';

it('confirms the account from a link on cold start', async () => {
  await device.launchApp({
    newInstance: true,
    url: 'mealkit://signup/confirm?token=test',
  });
  await expect(element(by.id('signup.confirmed'))).toBeVisible();
});

go deeper

for a junior

Recall that launchApp starts or resumes the app, reloadReactNative reloads JavaScript, and openURL sends a link to the app.

for a middle

Explain the newInstance default, what reloadReactNative does not reset, and when launchApp({ url }) is needed instead of openURL.

for a senior

Structure beforeAll and beforeEach hooks so each test starts from known state, trading reload speed against native leftovers.

for a principal

Decide how much isolation a suite buys with relaunches and resets, against the time budget of the whole pipeline.

## The device API's three ways to (re)start A **Detox** test controls the app's lifecycle through the global `device` object. Three calls come up in every interview about test structure, and they reset very different amounts of state. | Call | What restarts | Native state kept | Cost | |---|---|---|---| | `device.launchApp()` | nothing if already running — it resumes | everything | low | | `device.launchApp({ newInstance: true })` | the whole app process | on-disk data (unless `delete` or `resetAppState`) | medium | | `device.reloadReactNative()` | only the JavaScript bundle | native process, native modules' in-memory state, on-disk data | low | | `device.openURL({ url })` | nothing; delivers a URL to the running app | everything | low | ## `device.launchApp(params)` `newInstance` **defaults to `false`**: if the app is running, Detox tries to resume it; if not, it launches a new instance anyway. Useful parameters: - **`newInstance: true`** — terminate and relaunch; a real cold start for the process. - **`delete: true`** — uninstall and reinstall first; slow but guarantees fresh data. - **`resetAppState: true`** — reset data without necessarily a full reinstall: on Android it clears app data with `pm clear`, on iOS it uninstalls and reinstalls. - **`url`** — launch with a URL, for testing how the app handles a link on start; combine with `newInstance: true` for a cold start. - **`launchArgs`** — key-value arguments the app can read at launch, for example to point at a test server or turn off onboarding. - **`permissions`** (iOS only) — grant or deny permissions such as `notifications` or `location` up front, so a system alert does not block the test. ## `device.reloadReactNative()` This reloads the JS bundle inside the running native app. It is much faster than a relaunch and resets JavaScript state — the navigation tree, React state, in-memory stores. It does **not** reset native state, and the Detox docs warn that it "does not work without faults": under some conditions it misbehaves or even crashes, in which case `device.launchApp()` is the fallback. The starter test that `detox init` generates uses it in `beforeEach`, which is a reasonable default for simple suites but not a guarantee of isolation. ## `device.openURL({ url })` This delivers a URL to the **already running** app, the way tapping a link in another app would, so the test can check that `mealkit://plans/family` lands on the right screen. For the cold-start path, use `launchApp({ url, newInstance: true })` instead. `sourceApp` is an optional, iOS-only parameter naming the app the URL came from. How the app routes that URL is its own concern; the Detox call only delivers it. ## Simulating a return from the background `device.sendToHome()` sends the app to the background, and a following `device.launchApp({ newInstance: false })` brings it back — the Detox docs describe this pair as the way to simulate the app returning from the background. In a sign-up flow it answers a practical question: does the half-filled form survive the user checking their email for the confirmation code? Designing which app-state transitions deserve a test is a broader test-design question; the Detox calls themselves are just these two. ## Structuring the meal-kit sign-up suite 1. **`beforeAll`** — `await device.launchApp({ newInstance: true, permissions: { notifications: 'YES' } })` so the notification prompt never covers the form. 2. **`beforeEach`** — return to a known screen. `reloadReactNative()` is fast when all sign-up state lives in JavaScript; if the flow writes a token to secure storage or a native module holds state, prefer `launchApp({ newInstance: true })` plus a data reset for the tests that need a fresh user. 3. **A test for the emailed "confirm your account" link** — `await device.openURL({ url: 'mealkit://signup/confirm?token=test' })` while the app is running, then assert the confirmation screen; a second test does the same with `launchApp({ url, newInstance: true })` to cover a cold start. ```js describe('sign-up', () => { beforeAll(async () => { await device.launchApp({ newInstance: true, permissions: { notifications: 'YES' } }); }); beforeEach(async () => { await device.reloadReactNative(); }); it('confirms the account from a link while running', async () => { await device.openURL({ url: 'mealkit://signup/confirm?token=test' }); await expect(element(by.id('signup.confirmed'))).toBeVisible(); }); }); ``` ## What interviewers listen for That `launchApp` without options **resumes**, that `reloadReactNative` resets **only JavaScript** and is not fault-free, that `openURL` targets a running app while `launchApp({ url })` covers launch, and that the choice is a trade between speed and how much state each test must start without.

  • Why can reloadReactNative leave a sign-up test in the wrong state?
    It resets only JavaScript. A token written to secure storage, a native SDK's session or anything else held natively survives the reload, so the next test can start signed in. Relaunch with `newInstance: true`, and reset data with `resetAppState` or `delete` when the test needs a fresh user.
  • What does resetAppState do on each platform?
    On Android it clears the app's data with `pm clear`, which is usually faster than reinstalling. On iOS it uninstalls and reinstalls the app. Either way the app starts without stored data, and it is cheaper than a full `delete` cycle on Android.

saying these in an interview costs you the question

  • device.launchApp() always restarts the app from scratch.
  • reloadReactNative() clears secure storage and native module state.
  • openURL() is how you test a link that launches a closed app.
  • reloadReactNative() is guaranteed to be reliable for every app.
  • resetAppState behaves identically on iOS and Android.