skip to content

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.