AndroidJUnitRunner
Instrumentation tests run directly on a physical device or emulator, allowing them to access the actual Android framework APIs, context, and resources. The core of this execution is AndroidJUnitRunner.
When AndroidJUnitRunner starts, it loads both your test APK and the application APK into the same process. This allows your test code to poke directly into the memory space of the application it is testing, making it incredibly powerful for white-box testing within AOSP.
Espresso UI Testing
Espresso is the standard framework for writing robust UI tests. It automatically synchronizes with the main UI thread, ensuring that your test actions (like clicking a button) only occur when the UI is idle. This eliminates the need for brittle Thread.sleep() calls.
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
@RunWith(AndroidJUnit4.class)
public class SettingsUITest {
@Rule
public ActivityScenarioRule<SettingsActivity> activityRule =
new ActivityScenarioRule<>(SettingsActivity.class);
@Test
public void testToggleAirplaneMode() {
// Find the toggle by ID and click it
onView(withId(R.id.airplane_mode_toggle)).perform(click());
// Assertions would follow here
}
}
In AOSP, Espresso is used extensively to test built-in applications like Settings, SystemUI, and the Launcher.
UIAutomator for Cross-App Testing
While Espresso is limited to testing a single application within its own process, UIAutomator allows you to interact with the entire system screen. It acts more like a user, finding UI elements based on their accessibility labels or text, regardless of which app is currently in the foreground.
UIAutomator is crucial for testing workflows that cross application boundaries, such as:
- Opening a notification.
- Switching to the Settings app to revoke a permission.
- Returning to the original app to verify it handles the revoked permission correctly.
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject;
import androidx.test.uiautomator.UiSelector;
@Test
public void testSystemNavigation() throws Exception {
UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
// Press the physical home button
device.pressHome();
// Find the Chrome app by text and launch it
UiObject chromeApp = device.findObject(new UiSelector().text("Chrome"));
if (chromeApp.exists()) {
chromeApp.click();
}
}
Instrumentation Tests in AOSP Build System
Integrating instrumentation tests into AOSP requires an Android.bp definition.
android_test {
name: "SystemUITests",
srcs: ["src/**/*.java"],
certificate: "platform",
instrumentation_for: "SystemUI",
static_libs: [
"androidx.test.runner",
"androidx.test.espresso.core",
"ub-uiautomator",
],
}
The instrumentation_for tag is critical; it tells the build system which target package this test module is meant to instrument. Using certificate: "platform" ensures the test APK is signed with the same key as the system apps, granting it signature level permissions required to test deeply integrated features.