Testing Android Apps with Appium
Appium runs as a server that accepts WebDriver-protocol requests and translates them into UI actions on a connected Android device or emulator. The tests themselves are standard Java/Python/JS code; Appium handles the driver layer. This separation means you can reuse the same test logic across Android and iOS (mostly) by swapping the driver configuration.
Setup
Prerequisites: Node.js, npm, Android SDK, Android Studio (for the emulator), JDK, and a device or emulator with USB debugging enabled.
npm install -g appium
npm install -g appium-doctor
appium-doctor # checks your environment, fixes misconfigurations
appium driver install uiautomator2
appium # starts the server, listens on port 4723
appium-doctor is worth running before anything else. It’ll catch missing environment variables (ANDROID_HOME, JAVA_HOME) that would otherwise surface as cryptic errors mid-test.
The UiAutomator2 driver is the standard Android automation backend. It hooks into Android’s accessibility services to inspect and interact with UI elements.
Test Cases
Tests were written for a form-based Android app. The cases cover the range of input validation and UI interaction patterns you’d encounter in most apps:
testSendButtonVisibility: Fills all required fields and verifies the send button becomes visible. Clears one field and verifies the button disappears. Tests that the send button is gated on complete input, not just present by default.
testIncompleteInput: Submits with only some fields filled. Checks that default values appear where expected and that the app doesn’t silently accept incomplete data.
testInvalidBirthdayInput: Enters a birthday outside valid range and checks for an error state. The interesting edge case here is leap year dates and far-future dates: most validation logic handles obvious cases (year 1900) but misses the boundary conditions.
testGenderExclusivity: Selects one gender option, then another, and verifies the first deselects. Radio button behavior that you’d think is guaranteed by the widget type but occasionally isn’t when custom views are involved.
testAIModelCheckboxBehavior: Checks a checkbox and verifies a conditional text field appears. Unchecks and verifies it disappears. Conditional field visibility driven by checkbox state is a surprisingly common source of bugs when state management is handled manually.
What Works and What Doesn’t
Appium handles standard Android widgets (TextViews, EditTexts, Buttons, CheckBoxes) reliably. Custom views with non-standard accessibility implementations cause element-not-found errors, and there’s usually no clean fix; you end up using coordinate-based taps as a fallback, which is brittle.
The emulator adds latency. Animations that complete instantly on a device take 300-500ms on an emulator, which breaks timing-dependent tests unless you disable animations in developer options (Window animation scale, Transition animation scale, Animator duration scale all set to 0). I wasted an afternoon on this before realizing the issue.
Cross-platform reuse (Android + iOS) is theoretically possible but practically limited. Element locator strategies (id, accessibility id, xpath) diverge between platforms, so tests written for Android rarely run on iOS without modification. The shared architecture is the value, not literal code reuse.
Source code and test scripts on GitHub.