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?
answer
- instrumentation vs injection
- app APK plus test APK
- assembleAndroidTest -DtestBuildType
- default path under apk/androidTest/
- DetoxTest hands off to Detox.runTests
basics
~20 sOn 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.
solid answer
~40 sDetox needs native code inside the app process. On an iOS simulator it launches the `.app` and injects its own cached framework, so an `ios.app` entry needs only `binaryPath` and `build`. Android has no such injection, so Detox uses instrumentation: a second, test APK carries Detox's Android library and Espresso, and the Android test runner starts it next to the app APK. `DetoxTest.java` is the one JUnit test in that APK; it holds an activity rule for `MainActivity` and calls `Detox.runTests`, handing control to the JavaScript tests. The default build string runs `assembleDebug assembleAndroidTest -DtestBuildType=debug`, and when `testBinaryPath` is omitted Detox derives the path, e.g. `apk/androidTest/debug/app-debug-androidTest.apk`.
code
javascript · 12 linesmodule.exports = {
apps: {
'android.debug': {
type: 'android.apk',
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
// optional: derived as apk/androidTest/debug/app-debug-androidTest.apk when omitted
testBinaryPath: 'android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk',
build: 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
reversePorts: [8081],
},
},
};go deeper
Recall that Android needs an app APK and a test APK, and that DetoxTest.java is a one-off entry point, not where tests are written.
Explain instrumentation versus iOS framework injection, what assembleAndroidTest and -DtestBuildType do, and how Detox derives the test APK path.
Diagnose a missing test APK, crossed binary paths or a device that never connects, and know when a prebuilt test APK justifies testBinaryPath.
Judge how much native test scaffolding a team should own per platform, and whether managed Expo builds or hand-patched native projects fit the release pipeline better.
## Two binaries on Android, one on iOS A **Detox** test run needs code inside the app process that can find views, perform actions and report whether the app is idle. The two platforms get that code into the app in different ways, which is why an Android app config in `.detoxrc.js` asks for more than an iOS one. - **iOS simulator.** Detox launches the `.app` itself and injects its own framework into the process at launch, using a Detox framework it builds and caches per Xcode and Detox version on the Mac. The Xcode project does not need a test target for Detox, so an `ios.app` entry only needs `binaryPath` and a `build` command. - **Android.** Android has no equivalent injection. Test code runs through **instrumentation**: a second APK, the **test APK**, is installed alongside the **app APK** and started by the Android test runner in the same process as the app. The test APK carries Detox's native Android library, Espresso and the project's own instrumentation test class. ## What the Android project gains The Detox setup guide patches a bare React Native project in a few places: 1. **Build scripts.** `android/build.gradle` adds a Maven repository pointing at `node_modules/detox/Detox-android` (plus the Kotlin Gradle plugin). `android/app/build.gradle` adds the Detox Android library as an `androidTestImplementation` dependency, sets `testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'` and sets `testBuildType System.getProperty('testBuildType', 'debug')`, so the build command can choose which build type the test APK targets. 2. **`DetoxTest.java`.** A single JUnit test under `android/app/src/androidTest/java/<package>/`. It is not where test steps live — those stay in the JavaScript files — but the entry point: it declares an activity rule for the app's main activity and calls `Detox.runTests(rule, detoxConfig)`, handing control to Detox for the whole run. Its `DetoxConfig` also sets idle-policy timeouts and `rnContextLoadTimeoutSec` (180 seconds for debug builds and 60 for release in the template). 3. **Network security config.** Detox's native side on the device talks to the Node.js test process on the host over an unencrypted local connection, so a `network_security_config.xml` permits cleartext traffic to `localhost` and `10.0.2.2` only. ```java @RunWith(AndroidJUnit4.class) @LargeTest public class DetoxTest { @Rule public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false); @Test public void runDetoxTests() { DetoxConfig detoxConfig = new DetoxConfig(); detoxConfig.rnContextLoadTimeoutSec = (BuildConfig.DEBUG ? 180 : 60); Detox.runTests(mActivityRule, detoxConfig); } } ``` A wrong package name or a custom main activity that is not `MainActivity` is the usual reason this file fails to compile or never starts the app. ## Building and locating the test APK The default Android `build` string in `.detoxrc.js` builds both APKs in one Gradle call: `./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug` (or the `Release` / `release` pair). `assembleAndroidTest` produces the instrumentation APK; `-DtestBuildType` makes it target the same build type as the app APK. **`testBinaryPath`** is optional. When it is absent, Detox derives the test APK's path from `binaryPath`: | `binaryPath` | Derived test APK path | |---|---| | `android/app/build/outputs/apk/debug/app-debug.apk` | `android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk` | | `android/app/build/outputs/apk/release/app-release.apk` | `android/app/build/outputs/apk/androidTest/release/app-release-androidTest.apk` | The derivation also handles product-flavor names embedded in the file name. With flavors, both the build string and the paths change per flavor: a `driver` flavor builds with `assembleDriverDebug assembleDriverDebugAndroidTest -DtestBuildType=debug`, and its `binaryPath` becomes `apk/driver/debug/app-driver-debug.apk`, so each flavor gets its own app entry and usually its own configuration. Set `testBinaryPath` explicitly when the test APK is produced by a separate flow — for example, prebuilt once and reused across several app variants — or lives in a non-default folder. ## Typical failures - **"The test APK could not be found at path …"** — the build string ran `assembleDebug` without `assembleAndroidTest`, or the derived path does not match a custom output location. The error's hint points at the build command. - **App and test APK swapped** — Detox inspects both binaries before installing and logs a warning when a test APK looks like an app APK, which usually means `binaryPath` and `testBinaryPath` were crossed. - **Instrumentation starts but the app never connects** — often a missing network security config, so the device-side client cannot reach the host. ## Expo projects The Detox setup guide covers bare React Native projects. For an Expo project it points to Expo's own end-to-end testing guide instead, because the native projects there are set up differently.
- When would you set testBinaryPath explicitly?When the test APK does not sit where Detox derives it from `binaryPath` — for example, a pipeline that prebuilds the instrumentation APK once and reuses it across several app variants, or a custom Gradle output folder. In a standard React Native project the derived `apk/androidTest/<buildType>/…-androidTest.apk` path is correct and the key can be omitted.
- Why does DetoxTest.java need the right package and activity name?It is compiled into the test APK under the app's package and its activity rule names the activity Detox launches. A copied template with the wrong package does not compile against the app's `BuildConfig`, and a custom main activity that is not `MainActivity` means the rule targets a class that does not exist.
saying these in an interview costs you the question
- testBinaryPath is mandatory in every Android app config.
- The test APK contains the JavaScript test files.
- DetoxTest.java is where the Android end-to-end steps are written.
- Detox on Android drives the app purely through adb input commands.
- Allowing cleartext to localhost makes all production traffic unencrypted.