How to Test Login Flow on Android (Complete Guide, 2026)
Login is the highest-stakes flow in almost every app. Ship a broken login and users cannot reach anything else. Ship a flaky login and retention craters. This guide covers what to test, how to test it
Login is the highest-stakes flow in almost every app. Ship a broken login and users cannot reach anything else. Ship a flaky login and retention craters. This guide covers what to test, how to test it manually, how to automate it, and the edge cases that production catches but staging misses.
Why login testing is harder than it looks
A simple "username + password + sign in" form hides several real systems: network calls, token storage, biometric integration, session refresh, error handling, and input validation. Bugs usually come from the interaction between these, not from any single piece.
Common production incidents:
- Token stored, but not loaded on cold start — user re-prompted every launch
- Biometric unlock bypasses password check — security regression
- "Wrong password" toast disappears in 200 ms, user does not read it
- OTP input auto-submits on paste but not on type — inconsistent UX
- Session expired mid-flow — app crashes instead of re-prompting
What to test
Happy path
- Correct credentials → successful login → lands on home screen
- Correct credentials + "remember me" → no re-prompt on next launch
- OAuth / social login → token exchange → user profile populated
- OTP login → OTP received → correct code → authenticated
Error paths
- Wrong password → clear error message visible for at least 3 seconds
- Wrong username → same behavior (never leak "user exists")
- Locked account → distinct error, link to recovery
- Empty fields → inline validation before network call
- Network timeout mid-login → loading state clears, retry offered
- Server 500 → user sees friendly error, not stack trace
Edge cases
- Password paste enabled (many apps disable it, breaking password managers)
- Paste of trailing space — rejected or trimmed?
- Unicode characters in username — RTL, emoji
- Very long password (128+ chars) — does the backend hash it or truncate silently?
- Rapid re-tap of "Sign In" — duplicate requests?
- Biometric enrolled and then removed from OS — fallback to password
- App backgrounded during login — resumes correctly on return
- Token expires while user is active — silent refresh or re-prompt?
Accessibility
- Labels and roles set for screen readers (
contentDescription) - Error messages announced, not just visible
- Focus order: username → password → sign in
- Touch targets at least 48dp × 48dp
Manual testing approach
Start with the happy path on a clean install. Clear app data. Launch, enter credentials, confirm home screen loads and user profile is correct. Force-close the app, reopen it — should land directly on home without re-auth.
Then work through the error paths deliberately. Wrong password, missing field, no network. For each, confirm the error is visible long enough to read, the field retains its value (no retyping the email), and the "Sign In" button becomes tappable again.
For OTP flows, test both the typed path and the paste path. Test receiving the OTP in another app (Messages) and using auto-fill. Test requesting a second OTP before the first one is used.
Automated testing
Appium (Android native)
def test_login_success(driver):
driver.find_element(AppiumBy.ID, "com.example:id/username").send_keys("test@example.com")
driver.find_element(AppiumBy.ID, "com.example:id/password").send_keys("correct-horse")
driver.find_element(AppiumBy.ID, "com.example:id/sign_in").click()
WebDriverWait(driver, 10).until(
EC.presence_of_element_located((AppiumBy.ID, "com.example:id/home_screen"))
)
Add tests for each error path with explicit assertions on the error message text. Use page_source snapshots for regression.
Compose / Jetpack
Use composeTestRule.onNodeWithTag("login_username") with semantic tags set in your composables. Testing in Compose is faster than instrumented tests but covers less of the real system.
How SUSA handles this
SUSA detects login screens automatically using input patterns (email/username + password, <=3 inputs, "sign in" keyword) and drives them with credentials you pass in at run time. It switches login methods if it sees "Continue with Email" or "Use phone instead" buttons. It handles OTP via static codes or TOTP secrets. Memory-based replay means once SUSA has logged in once, subsequent sessions skip the login phase.
Personas matter here: the elderly persona checks touch target sizes on the login form; the impatient persona flags slow login response; the adversarial persona mashes the sign-in button rapidly to check for duplicate-submit protection.
susatest-agent test myapp.apk --persona adversarial --username test@example.com --password correct-horse
What usually breaks in production
- Token refresh race condition — two network calls return simultaneously, second one overwrites good token with stale one. Repro only with variable network latency.
- Paste with auto-complete on — the paste drops the first character. Common on older Android keyboards.
- Biometric unlock missing liveness check — photo of user unlocks the app. Caught only by adversarial testing.
- "Remember me" persists password, not token — user's password recoverable from device storage.
- Silent session expiry — API returns 401, app eats the error and shows stale cached data.
Automate the common cases, run SUSA on the unknowns, do one manual pass per release for the edge cases that are genuinely hard to script. Three-layer coverage catches an order of magnitude more login bugs than scripts alone.
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free