Mobile apps have a lifecycle that web pages don't: they are launched, backgrounded, foregrounded, terminated, and reset. Bugs often hide in lifecycle transitions — a draft that disappears when the app is backgrounded, a token that expires after the app is killed, a crash that only happens on cold start after a low-memory event. Testing these scenarios requires specific Appium commands. This lesson covers every lifecycle operation you will need.
App launch
The simplest case: the app is not installed. Set the app capability to the APK or .app path, and Appium installs and launches it automatically when the session starts.
If the app is already installed and you just want to launch it without reinstalling, use appPackage + appActivity (Android) or bundleId (iOS):
Android:
UiAutomator2Options options = new UiAutomator2Options()
.setDeviceName("emulator-5554")
.setAppPackage("com.example.myapp")
.setAppActivity(".MainActivity");iOS:
XCUITestOptions options = new XCUITestOptions()
.setDeviceName("iPhone 15 Pro")
.setBundleId("com.example.myapp");The app must already be installed for this to work. No app path is needed.
Activating and terminating the app
// Bring the app to the foreground (or launch it if not running)
driver.activateApp("com.example.myapp"); // Android: package name
driver.activateApp("com.example.myapp"); // iOS: bundle ID — same method
// Kill the app process
driver.terminateApp("com.example.myapp");
// Check if the app is running (returns AppState enum)
ApplicationState state = driver.queryAppState("com.example.myapp");
// Possible states: NOT_INSTALLED, NOT_RUNNING, RUNNING_IN_BACKGROUND_SUSPENDED,
// RUNNING_IN_BACKGROUND, RUNNING_IN_FOREGROUNDBackgrounding the app
runAppInBackground sends the app to the background and restores it after the specified duration:
// Background for 5 seconds, then automatically restore
driver.runAppInBackground(Duration.ofSeconds(5));
// Background indefinitely (must call activateApp to restore)
driver.runAppInBackground(Duration.ofSeconds(-1));
driver.activateApp("com.example.myapp");Use this to test:
- Draft saving: enter text, background, foreground, verify draft is preserved
- Session timeout: background for longer than the session expiry window
- Push notification delivery: background the app, trigger a notification, verify badge or banner
- Background refresh: background, wait for data refresh interval, foreground, verify data updated
Resetting app state
Resetting is distinct from terminating. Terminating kills the process. Resetting clears user data.
Android:
// Clear all app data without uninstalling (fastest)
driver.executeScript("mobile: clearApp", Map.of("appId", "com.example.myapp"));
// Or via ADB
Runtime.getRuntime().exec(new String[]{
"adb", "shell", "pm", "clear", "com.example.myapp"
});iOS:
// Clear app data on Simulator
driver.executeScript("mobile: clearApp", Map.of("bundleId", "com.example.myapp"));Using Appium's built-in reset (cross-platform):
The noReset / fullReset capabilities control reset behaviour at session start. For programmatic reset during a test:
driver.resetApp(); // terminates and clears data per the reset capability settingsInstalling and uninstalling apps
// Check if installed
boolean isInstalled = driver.isAppInstalled("com.example.myapp");
// Install from a local path
driver.installApp("/path/to/app.apk");
// Remove the app
driver.removeApp("com.example.myapp");This is useful in @BeforeSuite to ensure a known-good version is installed, or in @AfterSuite to clean up.
Managing the Android back stack
// Press the Android back button
driver.pressKey(new KeyEvent(AndroidKey.BACK));
// Press the Android Home button (sends app to background)
driver.pressKey(new KeyEvent(AndroidKey.HOME));
// Open the recent apps drawer
driver.pressKey(new KeyEvent(AndroidKey.APP_SWITCH));Testing cold start vs warm start
A cold start is when the app process is not running at all. A warm start is when the app is already in memory (backgrounded). Cold start timing is a common performance metric.
// Simulate cold start: terminate then activate
driver.terminateApp("com.example.myapp");
long start = System.currentTimeMillis();
driver.activateApp("com.example.myapp");
// Wait for app to be ready (wait for specific element)
new WebDriverWait(driver, Duration.ofSeconds(10))
.until(ExpectedConditions.visibilityOfElementLocated(AppiumBy.accessibilityId("home_screen")));
long coldStartDuration = System.currentTimeMillis() - start;Common lifecycle test scenarios
| Scenario | Implementation |
|---|---|
| Draft preserved after background | runAppInBackground(5s) → verify field value |
| Login session survives app restart | terminateApp → activateApp → verify still logged in |
| Crash recovery | Force-kill process → relaunch → verify no data loss |
| First-run onboarding | fullReset: true in capabilities → verify onboarding shown |
| Deep link handling | driver.executeScript("mobile: deepLink", ...) → verify correct screen |
Deep link on Android:
driver.executeScript("mobile: deepLink", Map.of(
"url", "myapp://product/12345",
"package", "com.example.myapp"
));Deep link on iOS:
driver.executeScript("mobile: deepLink", Map.of(
"url", "myapp://product/12345",
"bundleId", "com.example.myapp"
));