Instrumented tests live in src/androidTest and ship as a second APK that runs in the same process as the app, driven by an instrumentation runner. BookNest's runner swaps the application class, because Hilt tests need HiltTestApplication instead of BookNestApp:
class HiltTestRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application =
super.newApplication(cl, HiltTestApplication::class.java.name, context)
}defaultConfig names it in testInstrumentationRunner, and the module adds androidTestImplementation entries for ui-test-junit4, androidx.test:runner 1.7.0, androidx.test.ext:junit 1.3.0 and hilt-android-testing, plus kspAndroidTest(hilt-compiler). ./gradlew connectedDebugAndroidTest builds both APKs, installs them on every connected device (ANDROID_SERIAL picks one) and runs them. Underneath it is two installs and one shell command, which is how they ran here after assembleDebugAndroidTest:
adb -s emulator-5556 install -r app/build/outputs/apk/debug/app-debug.apk
adb -s emulator-5556 install -r app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk
adb -s emulator-5556 shell am instrument -w -e class com.example.booknest.BookNestEndToEndTest \
com.example.booknest.test/com.example.booknest.HiltTestRunnercom.example.booknest.BookNestEndToEndTest:.. Time: 42.322 OK (2 tests)
Each dot is a passing test. The first run took 78.8 seconds while the phone compiled the new APKs, the second 42.3 on an emulator sharing four CPUs: the price of fidelity. On CI, Gradle 19,597 Managed Devices boot the emulator for you, and Firebase 1 Test Lab (commercial, free daily quota) runs the APKs on real phones.