skip to content

Device & App Configs

A .detoxrc.js pairs apps with devices into named configurations, each with its own build command and binary. Interviewers ask about the extra Android test build and emulator vs attached runs.

on this pageshow

explore

questions

5

In Detox, how do the devices, apps and configurations sections of .detoxrc.js let one suite run on an iOS simulator and an Android emulator?

level: middleimportance: must knowfreq 48%

answer

  1. three dictionaries, named pairs
  2. configuration = device alias + app alias
  3. ios.app vs android.apk, binaryPath
  4. detox build runs the build string
  5. detox test -c installs, never builds

basics

~20 s

A Detox config lists devices and apps as separate named dictionaries, then defines configurations that each pair one device with one app. The same test files run against whichever pair detox build -c and detox test -c select.

solid answer

~40 s

In `.detoxrc.js`, `devices` describes where the app runs (`ios.simulator` with a device type, `android.emulator` with an `avdName`), and `apps` describes which binary to install (`ios.app` or `android.apk`, each with a `binaryPath` and an optional `build` command). `configurations` names the pairs, such as `ios.sim.release` and `android.emu.release`. For both platforms I build each binary with `detox build -c <name>`, which just runs that app's `build` string, then run `detox test -c <name>` once per configuration, usually as two CI jobs. The test files are shared; the platform comes from the device `type`, not from the configuration's name. `detox test` never builds, so a stale binary at `binaryPath` runs stale native code.

code

javascript · 27 lines
javascript
/** @type {Detox.DetoxConfig} */
module.exports = {
  testRunner: {
    args: { $0: 'jest', config: 'e2e/jest.config.js' },
    jest: { setupTimeout: 120000 },
  },
  devices: {
    simulator: { type: 'ios.simulator', device: { type: 'iPhone 15' } },
    emulator: { type: 'android.emulator', device: { avdName: 'Pixel_7_API_35' } },
  },
  apps: {
    'ios.release': {
      type: 'ios.app',
      binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/MyApp.app',
      build: 'xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Release -sdk iphonesimulator -derivedDataPath ios/build',
    },
    'android.release': {
      type: 'android.apk',
      binaryPath: 'android/app/build/outputs/apk/release/app-release.apk',
      build: 'cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release',
    },
  },
  configurations: {
    'ios.sim.release': { device: 'simulator', app: 'ios.release' },
    'android.emu.release': { device: 'emulator', app: 'android.release' },
  },
};

go deeper

for a junior

Recall the three dictionaries in .detoxrc.js and that a configuration pairs one device with one app, selected with -c.

for a middle

Explain how aliases resolve, that the device type decides the platform, and that detox build only runs the app's build string while detox test only installs.

for a senior

Show how you would split iOS and Android runs into separate CI jobs from one config, and how a stale binaryPath silently tests old native code.

for a principal

Weigh how many device and build combinations a team can afford to run on every change versus nightly, and keep the config file small enough to reason about.

## Why Detox configuration is a matrix **Detox** is a gray-box end-to-end testing framework for React Native apps: tests written in JavaScript run in Node.js on the host machine and drive a real app on an iOS simulator or an Android emulator or device. Even a small app has several combinations worth testing: an iOS simulator with a debug build, the same simulator with a release build, and the same two builds on Android. Add a tablet or a second OS version and the combinations multiply. Detox handles this with a **static config file** — usually `.detoxrc.js` in the project root — built from three key-value dictionaries. ## The three dictionaries - **`devices`** — named device descriptions. Each has a `type` (`ios.simulator`, `android.emulator`, `android.attached`, `android.genycloud`) and a `device` query, such as `{ type: 'iPhone 15' }` for a simulator or `{ avdName: 'Pixel_7_API_35' }` for an emulator. - **`apps`** — named app descriptions. Each has a `type` (`ios.app` or `android.apk`), a **`binaryPath`** to the built `.app` or `.apk`, and an optional **`build`** shell command that produces it. Android apps may also carry `testBinaryPath` and `reversePorts`. - **`configurations`** — the named entry points. Each pairs one `device` alias with one `app` alias (or an `apps` array for multi-app tests), and may override global sections such as `testRunner`, `artifacts`, `behavior` or `logger` for that pair only. Configuration names are arbitrary strings. `ios.sim.release` and `android.emu.release` are conventions from the file `detox init` generates; the platform actually comes from the device's `type`, not from the name. | Section | Answers the question | Key fields | |---|---|---| | `devices` | Where does the app run? | `type`, `device` query | | `apps` | Which binary, and how is it built? | `type`, `binaryPath`, `build`, `testBinaryPath` | | `configurations` | Which pair is this run? | `device`, `app`, per-config overrides | ## Running one suite on both platforms The reserved scenario for this leaf is the everyday one: the same test files, one iOS simulator run and one Android emulator run. The workflow is: 1. Define one simulator device and one emulator device. 2. Define an `ios.app` and an `android.apk` app, each with its `binaryPath` and `build` command. 3. Define two configurations, for example `ios.sim.release` and `android.emu.release`. 4. Build each binary with `detox build -c <name>`, which simply runs that app's `build` string. 5. Run the suite with `detox test -c <name>` once per configuration, usually as two CI jobs. The test files do not change between the two runs. They use `element(by.id(...))`, `device` and the other Detox globals, which resolve to the platform of whichever configuration was selected. Where behaviour genuinely differs, a test can branch on `device.getPlatform()`, but most suites keep that to a minimum. ## What `detox build` and `detox test` do — and do not do `detox build` is a convenience: it executes the `build` command of the selected configuration's app and nothing else. With `--if-missing` it skips the build when the binary already exists. `detox test` never builds anything. It resolves the configuration, starts a Detox session and spawns the test runner; during the runner's setup Detox allocates a device and installs whatever binary sits at `binaryPath`. A stale binary therefore runs stale native code without any warning, which is a common source of confusion after a native change. ## How Detox picks a configuration The name comes from `-c` / `--configuration`, or from the `DETOX_CONFIGURATION` environment variable when Jest is started directly without the Detox CLI. If none is given, Detox uses `selectedConfiguration` from the config file; failing that, if `configurations` has exactly one entry, it uses that one. Otherwise it stops with a "Cannot determine which configuration to use" error that lists the available names. The config file itself is found by scanning the working directory for `.detoxrc.js`, `.detoxrc.json`, `.detoxrc`, `detox.config.js`, `detox.config.json` and finally a `detox` section in `package.json`, or it is given explicitly with `-C`. ## Useful refinements - **Inline devices and apps.** Aliases are optional; a configuration may embed the device and app objects directly, at the cost of duplication. - **`extends`.** A config can extend a shared preset module; the chain is deep-merged parent to child. - **`--device-name` / `-n`.** Overrides the device query at the command line, so one configuration can be pointed at another simulator model without a new entry. - **Per-configuration overrides.** A configuration may raise the logger level or change `testRunner.args` for that pair only. A candidate who can sketch this file from memory, explain that the device `type` decides the platform, and say that `detox test` installs but never builds, has shown the configuration model interviewers look for.

  • What happens if you run detox test without -c?
    Detox looks for `selectedConfiguration` in the config file. If that is absent and `configurations` has exactly one entry, it uses that entry. With several configurations and no default it stops with a "Cannot determine which configuration to use" error that lists the available names.
  • How do you run one configuration against a different simulator model without adding a new entry?
    Pass `-n` / `--device-name` to `detox test`. It overrides the device query of the selected configuration for that run, so an `iPhone 15` configuration can target another installed simulator type while keeping the same app and overrides.
  • Can one Detox configuration launch two apps?
    Yes, with limited support. A configuration can list an `apps` array instead of `app`; each app config then needs a `name`, and a test switches between them with `device.selectApp(name)` before `device.launchApp()`. They still share the configuration's single device.

A configuration is like a seat reservation that pairs a passenger with a specific train: the passenger list (apps) and the timetable (devices) are kept separately, and each ticket (configuration) names one of each.

saying these in an interview costs you the question

  • The configuration name prefix like ios. tells Detox which platform to use.
  • detox test builds the app before installing it.
  • One configuration runs the suite on iOS and Android at the same time.
  • Each platform needs its own copy of the e2e test files.
  • binaryPath points at the Xcode or Gradle project folder.
open as a page

In a Detox 20 project, what does the detox test command actually run, and what does Jest contribute that Detox itself does not?

level: juniorimportance: should knowfreq 30%

basics

~20 s

detox test is a wrapper: it resolves the chosen configuration, starts a Detox session and spawns Jest, by default jest --config e2e/jest.config.js. Jest finds and runs the test files; Detox's Jest environment boots the device, installs the app and supplies device, element and expect.

open as a page

Why does a Detox Android app config need a second test APK and a DetoxTest.java class when an iOS app config needs only the .app?

level: middleimportance: should knowfreq 32%

basics

~20 s

On Android, Detox's native code runs as instrumentation: a separate test APK, built with assembleAndroidTest and started through the single DetoxTest.java JUnit test, runs inside the app process. On an iOS simulator Detox injects its framework into the .app at launch instead.

open as a page

In Detox, what changes when a configuration uses an android.attached device instead of android.emulator, and why does CI usually choose the emulator?

level: seniorimportance: should knowfreq 24%

basics

~20 s

An android.emulator device names an AVD that Detox can boot, prepare and shut down itself; an android.attached device is an adb serial pattern Detox can only pick from already-connected devices. CI prefers emulators because they are reproducible and Detox manages them.

open as a page

A Detox suite passes on android.emu.debug but the android.emu.release configuration hangs at launch — why, and why test release builds at all?

level: seniorimportance: should knowfreq 30%

basics

~20 s

A release build with shrinking on strips React Native classes Detox reaches by reflection, so the app hangs or crashes; adding Detox's proguard-rules-app.pro fixes it. CI still tests release because it is Metro-free, deterministic and closest to what ships.

open as a page