How to Test Forgot Password on Android (Complete Guide)
Forgot‑password flow is one of the most frequently used recovery mechanisms in mobile apps. When it fails, users are locked out, support tickets spike, and brand trust erodes. In production, the flow
Why Forgot Password Testing Matters
Forgot‑password flow is one of the most frequently used recovery mechanisms in mobile apps. When it fails, users are locked out, support tickets spike, and brand trust erodes. In production, the flow often hides subtle bugs that unit tests miss: race conditions between network calls and UI updates, mishandled error messages from the backend, or accessibility barriers that prevent screen‑reader users from completing the reset. Because the flow touches network, credential storage, UI state, and sometimes biometric fallback, a defect can cascade into security issues such as token leakage or brute‑force exposure. A systematic test strategy therefore protects both user experience and application security.
Comprehensive Test Matrix
Below is a matrix that groups test ideas by category, sub‑category, and expected outcome. Use it as a checklist when designing manual or automated suites. Each row can be expanded into a test case with pre‑conditions, steps, and verification points.
| Category | Sub‑category | Test ID | Description | Expected Result |
|---|---|---|---|---|
| Happy Path | Valid email entry | HP‑01 | User taps “Forgot password”, enters a registered email, submits, receives success toast, and sees a confirmation screen. | Success message displayed, email sent (verify via test mailbox). |
| Valid phone entry (if supported) | HP‑02 | Same as HP‑01 but using phone number. | Success message, SMS sent. | |
| Deep link from email | HP‑03 | User clicks reset link in email, lands on password‑set screen, enters new password twice, submits. | Password updated, redirected to login screen with fresh credentials. | |
| Error Paths | Unregistered email | EP‑01 | Enter email not associated with any account. | App shows “No account found” or similar, does not leak existence of account. |
| Malformed email | EP‑02 | Enter “user@”, “@@”, or empty string. | Inline validation error appears before submission. | |
| Network timeout | EP‑03 | Simulate latency >10 s or drop connection after submit. | App shows retryable error, does not crash, allows user to retry. | |
| Server error 500 | EP‑04 | Backend returns HTTP 500. | Generic error toast, no stack trace shown to user. | |
| Rate‑limit hit | EP‑05 | Submit request 5 times quickly (if limit is 4/min). | App shows “Too many attempts, try later” and blocks further submissions for the cooldown period. | |
| Edge Cases | Special characters in email | EC‑01 | Email contains ‘+’, ‘‑’, ‘.’, Unicode (e.g., 用户@例子.cn). | Submission succeeds if backend accepts; validation does not reject incorrectly. |
| Leading/trailing spaces | EC‑02 | User pastes “ user@example.com ” with spaces. | App trims internally or shows validation error; never sends spaces to server. | |
| Clipboard paste | EC‑03 | User pastes email from clipboard that includes hidden characters. | App sanitizes input; no crash. | |
| Orientation change mid‑flow | EC‑04 | Rotate device after entering email but before submitting. | Entered text persists, UI adapts, no loss of state. | |
| Multi‑window mode | EC‑05 | Launch app in split‑screen, start forgot‑password flow in one pane. | Flow works independently, no interference with other pane. | |
| Biometric fallback | EC‑06 | Device has fingerprint enabled; user cancels biometric prompt and falls back to password entry for reset confirmation. | Flow continues with manual entry, no dead end. | |
| Accessibility | TalkBack navigation | AC‑01 | Enable TalkBack, navigate to “Forgot password” button, activate, fill fields, submit. | All controls announced, hints appropriate, focus moves correctly after each action. |
| Contrast ratio | AC‑02 | Verify text and button colors meet WCAG AA (≥4.5:1). | Contrast passes automated check (e.g., using Android Accessibility Test Framework). | |
| Touch target size | AC‑03 | Ensure tappable elements ≥48 dp. | No false positives from lint rules. | |
| Error announcement | AC‑04 | Trigger validation error; TalkBack announces error message. | Error spoken immediately when field loses focus. | |
| Security & Privacy | Token leakage in logs | SP‑01 | Capture logcat while submitting reset request. | No password reset token, email, or user‑ID appears in plain text. |
| Secure storage of temporary token | SP‑02 | After successful email submission, check app’s SharedPreferences or Keystore for any persisted reset token. | Token is not stored; only transient in‑memory variable. | |
| Brute‑force protection | SP‑03 | Automated script attempts 20 rapid resets with different emails. | Backend enforces rate limit or CAPTCHA after threshold; app does not expose timing differences that aid enumeration. | |
| Email enumeration mitigation | SP‑04 | Compare response times and messages for registered vs. unregistered emails. | No distinguishable difference (same UI message, similar timing). | |
| HTTPS enforcement | SP‑05 | Use a proxy to attempt HTTP request to reset endpoint. | Request is blocked or upgraded to TLS; no fallback to plaintext. |
How to Use the Matrix
- Manual testers: Pick a row, set up the test data (e.g., a disposable email account), follow the steps, and log pass/fail.
- Automation engineers: Map each Test ID to a test method; parameterize email/password values via a data‑provider.
- Regression suite: After each release, run the full matrix; any new failure flags a regression in the forgot‑password flow.
Manual Testing Approach
Setup
- Test environment – Use a physical device or emulator running Android 9 (API 28) or higher.
- Test accounts – Create at least two accounts in the backend: one with a known email/phone, one without.
- Mail/SMS catcher – Configure a service like Mailinator, Ethereal Email, or a local SMTP server to capture reset messages.
- Logging tools – Enable
adb logcatwith filters for your app’s tag and forNetworkSecurityConfigif you use clear‑text traffic debugging. - Accessibility scanner – Install the Android Accessibility Test Framework (ATF) or use TalkBack directly.
Step‑by‑Step Procedure
- Launch the app – Ensure you are on the login screen.
- Trigger forgot‑password – Tap the “Forgot password?” link/button. Verify that the navigation animation completes and the focus lands on the email/phone input field (check with TalkBack or
adb shell uiautomator dump). - Happy path –
- Input a valid registered email.
- Press the “Send reset link” button.
- Observe a toast or snackbar confirming that the email was sent.
- Switch to the mail catcher, verify receipt of the reset link within 30 seconds.
- Click the link; the app should open directly to the password‑set screen (handle App Links or custom scheme).
- Enter a new password that satisfies policy, repeat in confirmation field, press “Reset”.
- Expect a success message and automatic redirection to the login screen.
- Attempt login with the new credentials – should succeed.
- Error paths – Repeat steps 2‑3, substituting the test data from the EP rows. For each, verify that:
- Inline validation appears before submission (if applicable).
- No crash or ANR occurs.
- The UI returns to a state where the user can retry.
- Edge cases – Perform EC‑01 through EC‑06. Pay special attention to:
- Rotation: use
adb shell settings put system accelerometer_rotation 0then manually rotate, or use the emulator’s rotate shortcut. - Multi‑window: drag the app to the top half of the screen, launch another app in the bottom half, and interact with the flow.
- Biometric fallback: set up a fingerprint, then cancel the prompt when it appears; ensure the app shows the manual entry option.
- Accessibility checks –
- Enable TalkBack (
Settings > Accessibility > TalkBack). - Swipe to reach the forgot‑password button; double‑tap to activate.
- Move focus through each field; listen for hints (“Enter your email address”).
- Trigger an error (e.g., leave field blank) and confirm TalkBack reads the error message instantly.
- Use the Accessibility Scanner app to generate a contrast report; note any failures.
- Security & privacy checks –
- Run
adb logcat -v threadtime | grep -i resetwhile submitting; manually scan output for email addresses, tokens, or passwords. - After a successful email submission, inspect
SharedPreferencesviaadb shell run-as– ensure no reset token is stored.cat shared_prefs/ .xml - Use a proxy (e.g., mitmproxy) to confirm that all reset endpoints are accessed via
https://.
Document each step with screenshots or short video clips; attach the logcat snippets for any failure. This manual baseline becomes the oracle for automated tests.
Automated Testing on Android
Appium Basics
Appium drives the UI via the UiAutomator2 backend, making it suitable for cross‑framework (Java/Kotlin, Flutter, React Native) apps.
// ForgotPasswordTest.java
import io.appium.java_client.AppiumDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.remote.DesiredCapabilities;
import java.net.URL;
import java.util.concurrent.TimeUnit;
public class ForgotPasswordTest {
private AppiumDriver<MobileElement> driver;
@Before
public void setUp() throws Exception {
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability("platformName", "Android");
caps.setCapability("deviceName", "Pixel_4_API_33");
caps.setCapability("appPackage", "com.example.myapp");
caps.setCapability("appActivity", ".ui.LoginActivity");
caps.setCapability("automationName", "UiAutomator2");
driver = new AndroidDriver<>(new URL("http://localhost:4723/wd/hub"), caps);
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
@After
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
@Test
public void testHappyPath() {
// Navigate to forgot password
MobileElement forgotBtn = driver.findElement(By.id("forgot_password_link"));
forgotBtn.click();
// Enter email
MobileElement emailField = driver.findElement(By.id("email_input"));
emailField.sendKeys("testuser@example.com");
// Submit
MobileElement submitBtn = driver.findElement(By.id("send_reset_button"));
submitBtn.click();
// Verify toast
MobileElement toast = driver.findElement(By.xpath("//android.widget.Toast[@text='Reset link sent']"));
assert toast.isDisplayed();
// In a real test you would poll a mail server here; omitted for brevity.
}
}
Key points
- Use
UiAutomator2for better performance on Android 6+. - Replace hard‑coded IDs with accessibility IDs (
content-desc) when possible to improve resilience. - Extract email submission verification to a separate helper that polls an IMAP/SMTP test mailbox (e.g., using JavaMail).
Espresso UI Tests
Espresso runs within the same process as the app, giving fast feedback and direct access to ViewMatchers. It is ideal for regression suites that run on every PR.
// ForgotPasswordTest.kt
@LargeTest
@RunWith(AndroidJUnit4::class)
class ForgotPasswordTest {
@get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
@Test
fun `happy path reset password`() {
// Click forgot password
onView(withId(R.id.forgot_password_button)).perform(click())
// Type email
onView(withId(R.id.email_edit_text))
.perform(replaceWith("valid@example.com"), closeSoftKeyboard())
// Submit
onView(withId(R.id.send_button)).perform(click())
// Verify toast
onView(withText("Reset link sent"))
.inRoot(isToast())
.check(matches(isDisplayed()))
// In test, trigger deep link to reset screen via Intent
val resetIntent = Intent(Intent.ACTION_VIEW, Uri.parse("myapp://reset?token=FAKE"))
activityRule.scenario.onActivity { it.sendBroadcast(resetIntent) }
// Fill new password
onView(withId(R.id.password_edit_text))
.perform(replaceWith("NewP@ssw0rd!"), closeSoftKeyboard())
onView(withId(R.id.confirm_edit_text))
.perform(replaceWith("NewP@ssw0rd!"), closeSoftKeyboard())
onView(withId(R.id.reset_button)).perform(click())
// Expect navigation to login
onView(withId(R.id.login_button)).check(matches(isDisplayed()))
}
// Helper matcher for toast
private fun isToast(): Matcher<Root> {
return object : TypeSafeMatcher<Root>() {
override fun matchesSafely(root: Root): Boolean {
return root.windowLayoutParams?.type == TYPE_TOAST
}
override fun describeTo(description: Description) {
description.appendText("is toast")
}
}
}
}
Tips
- Use
IdlingResourceto wait for network responses if your app uses RxJava or Coroutines. - Parameterize the test with
@ParameterizedTest(via JUnit‑Params) to run the same flow with different email formats. - Leverage
ActivityScenarioto launch the app directly into the forgot‑password screen via a deep‑link intent, reducing test flakiness.
UIAutomator Scripts
For black‑box testing where you cannot modify the APK, UIAutomator works well. Below is a simple JUnit‑based script that validates the error path for an unregistered email.
// ForgotPasswordErrorTest.java
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject;
import androidx.test.uiautomator.UiObjectNotFoundException;
import androidx.test.uiautomator.UiSelector;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class ForgotPasswordErrorTest {
private UiDevice device;
@Before
public void setUp() {
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
// Assume we are already on login screen; otherwise launch via intent
}
@After
public void tearDown() {
// No special cleanup needed
}
@Test
public void testUnregisteredEmailShowsMessage() throws UiObjectNotFoundException {
// Click forgot password
new UiObject(new UiSelector().descriptionContains("Forgot password")).click();
// Enter email
UiObject emailField = new UiObject(new UiSelector().resourceId("com.example.myapp:id/email_input"));
emailField.setText("unknown@domain.com");
// Submit
new UiObject(new UiSelector().resourceId("com.example.myapp:id/send_button")).click();
// Wait for error toast
UiObject toast = new UiObject(new UiSelector().className("android.widget.Toast"));
assertTrue(toast.waitForExists(5000));
String toastText = toast.getText();
assertTrue(toastText.contains("No account found") || toastText.contains("We could not find"));
}
}
Run with:
adb install -r app-debug.apk
adb shell am instrument -w -r -e debug false -e class com.example.tests.ForgotPasswordErrorTest androidx.test.runner.AndroidJUnitRunner
CI Integration
- Server – Use a Linux VM with Android SDK, emulator (or Firebase Test Lab), Node.js for Appium, and JDK.
- Pipeline – Example GitHub Actions snippet:
name: Android Forgot Password Tests
on:
push:
branches: [main]
pull_request:
jobs:
ui-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: '11'
- name: Install Android SDK
uses: android-actions/setup-android@v2
- name: Start emulator
run: |
echo "no" | avdmanager create avd -n test -k "system-images;android-33;google_apis;x86_64"
emulator -avd test -no-window -no-audio &
./adb wait-for-device
./adb shell input keyevent 82 # Menu
- name: Run Appium tests
run: |
npm ci
npx appium &
./mvnw test -Dtest=ForgotPasswordTest
- name: Run Espresso tests
run: ./gradlew connectedAndroidTest
Adjust the steps for your build system (Gradle, Maven, Bazel). The key is to start the emulator or use a cloud device farm, launch Appium (if needed), and execute the test suites. Collect JUnit reports and publish them as artifacts for triage.
Production‑Only Edge Cases
Some defects surface only when the app runs under real‑world conditions: fluctuating networks, localized resources, or device‑specific behaviors. Below are concrete scenarios and how to reproduce them in a controlled lab.
| Issue | Trigger | Observation | Mitigation |
|---|---|---|---|
| Network flakiness | Use tc or netem to add 200 ms latency and 5 % packet loss after the reset request is sent. | App shows indefinite spinner, eventually crashes with NullPointerException when trying to parse a partial JSON response. | Implement proper timeout handling, show retry UI, and validate JSON schema before use. |
| Backend rate limiting | Send 8 reset requests within 30 seconds (limit set to 5/min). | After the 5th request, the backend returns HTTP 429; the app displays raw HTML error page instead of a user‑friendly message. | Map HTTP 429 to a localized “Too many attempts” toast and disable the submit button for the cool‑down period. |
| Localized strings | Change device language to Arabic (right‑to‑left). | The email field hint is misaligned, and the “Send” button overlaps the input, causing a missed tap. | Use android:autoSizeTextType="uniform" and test with RTL locales; ensure layout uses start/end not left/right. |
| Biometric fallback race | Device has fingerprint enabled; user cancels the biometric prompt after 2 seconds, then quickly types email. | App temporarily disables the email field, leading to a stuck UI where the keyboard never appears. | Decouple biometric flow from UI state machine; always re‑enable inputs on cancel. |
| Backup restore | User backs up app data via ADB, wipes device, restores backup, then attempts forgot‑password. | Restored SharedPreferences contain a stale reset token; app tries to reuse it and sends malformed request, causing 400 error. | Clear any transient auth tokens on app start; do not persist reset‑related data across sessions. |
| Deep link hijacking | Malicious app registers same scheme (myapp://) and intercepts the reset link. | Reset token is leaked to the other app; the legitimate app never receives the intent. | Use Android App Links with domain verification, or Firebase Dynamic Links, and verify the incoming intent’s package matches yours. |
| Storage scarcity | Fill device storage to <10 MB before triggering reset. | App fails to write temporary crypto material to cache, throws IOException and crashes. | Catch low‑storage exceptions, inform user to free space, and fallback to in‑memory operations where possible. |
Reproducing in CI
- Network: Use Docker container with
tc qdisc add dev eth0 root netem delay 200ms loss 5%. - Rate limit: Point the app to a mock server (e.g., WireMock) programmed to return 429 after N calls.
- Locale: Set
adb shell setprop persist.sys.language ar && adb shell setprop persist.sys.country EG && stop && start. - Storage: On emulator, extend the userdata image size and fill via
dd if=/dev/zero of=/data/local/tmp/junk bs=1M count=500.
Automate these as separate test suites tagged prod-edge; run them nightly rather than on every commit to keep feedback loops fast.
Accessibility and Security Considerations
WCAG Checks
| WCAG criterion | How to test on Android | Pass condition |
|---|---|---|
| 1.3.1 Info and Relationships | Enable TalkBack, navigate to forgot‑password screen; verify that labels (content-desc) correctly describe input fields and buttons. | All controls announce purpose; no ambiguous “button” without context. |
| 1.4.3 Contrast (Minimum) | Use Android Studio’s Layout Inspector or the accessibility-test-framework to compute contrast ratios for text vs. background. | Ratio ≥ 4.5:1 for normal text, ≥ 3:1 for large text. |
| 2.4.7 Focus Visible | With TalkBack off, use Tab key (via a keyboard) or D‑pad to move focus; ensure a visible highlight appears on interactive elements. | Focus outline visible and ≥ 2 dp width. |
| 3.2.1 On Focus | Change focus to email field; verify no automatic submission or context change occurs. | No unexpected navigation when focus moves. |
| 3.3.2 Labels or Instructions | Ensure each input field has an associated android:hint or labelFor attribute. | Hint present and announced by TalkBack. |
| 4.1.2 Name, Role, Value | Inspect UI hierarchy via uiautomator dump; confirm that editable fields have role EDITTEXT and buttons have role BUTTON. | Roles correctly exposed. |
Automate these checks with the Android Accessibility Test Framework (A11yTest) which can be added as an instrumentation test:
@RunWith(AndroidJUnit4::class)
class AccessibilityTest {
@get:Rule
val activityRule = ActivityScenarioRule(ForgotPasswordActivity::class.java)
@Test
fun forgotPasswordScreenIsAccessible() {
onView(withId(R.id.email_input)).check(matches(isDisplayed()))
// Use the A11yTest library
AccessibilityValidator.validate(activityRule.scenario)
}
}
Security Checks
| Check | Method | Expected outcome | |
|---|---|---|---|
| Token in memory only | Use Android Studio’s Memory Profiler to allocate a large heap, trigger reset, then dump heap (adb shell am dumpheap ). Analyze with MAT; ensure no plain‑text token strings appear. | No token strings in heap dump. | |
| Secure network | Run `adb logcat | grep -i "Cleartext"` while the app performs the reset request. | No clear‑text warnings; all traffic uses TLS. |
| No logging of PII | Add a logcat filter for your app’s tag and search for email patterns (\b[\w.+-]+@[\w-]+\.[\w.-]{2,}\b). | Zero matches after submitting reset request. | |
| Rate limiting on client | Instrument a click‑counter on the submit button; after 5 rapid clicks, verify that the button becomes disabled and a cooldown timer is shown. | Button disabled, timer visible. | |
| Certificate pinning validation (if used) | Burp Suite with a custom CA; attempt to intercept reset request. | Connection fails, app shows network error, not the reset UI. |
Implement these as unit tests where possible (e.g., using MockWebServer to assert that the Authorization header never contains a reset token) and as UI tests for client‑side enforcement.
Autonomous Persona‑Driven Exploration
How Personas Work
Autonomous QA platforms simulate real‑world users by defining personas—behavioral profiles that influence how the agent interacts with the app. Each persona decides:
- Exploration depth (how many screens to visit before stopping)
- Interaction speed (timid vs. rapid taps)
- Error tolerance (whether to persist after a validation failure)
- Input style (prefers pasting, uses voice, avoids special characters)
- Accessibility needs (enables TalkBack, changes font scale)
When the agent encounters a forgot‑password entry point, it will follow the persona’s policy: a curious user may try multiple email formats, an impatient user may spam the submit button, an elderly user may increase font size and rely on voice input, and an adversarial user may attempt SQL‑injection strings or extremely long payloads to probe for crashes.
What SUSA Adds
SUSA (SUSATest) is an autonomous QA agent that you can point at an APK or a web URL. After you upload the build, it:
- Discovers the forgot‑password flow without any test scripts—by following links, interpreting UI labels, and trying typical recovery patterns.
- Executes the flow through each of its built‑in personas, collecting metrics such as success rate, time to completion, and occurrence of crashes or ANRs.
- Flags anomalies that are invisible to scripted tests: e.g., a persona that pastes from clipboard triggers a hidden
NullPointerExceptionwhen the app fails to sanitize hidden Unicode characters; an impatient persona’s rapid taps reveal a race condition where the submit button remains enabled while a network request is in flight, allowing duplicate requests. - Generates regression scripts (Appium for Android, Playwright for Web) that capture the exact interaction sequences that caused the failure, giving developers a reproducible starting point.
- Learns across runs: if a particular screen was marked as a dead end by the novice persona, subsequent runs skip exploring it unless a new UI change suggests a revised path.
Because the agent does not rely on pre‑written test cases, it can surface bugs that only appear under atypical usage patterns—precisely the kind of issues that escape manual checklists and automated regression suites that follow a happy‑path script.
Example Findings from a Real‑World Run
During a recent exploration of a fintech app’s forgot‑password flow, SUSA’s adversarial persona submitted the following payload in the email field:
' OR 1=1;--
The app passed the string straight to the backend’s authentication service, which returned a SQL error that was rendered in the UI as a toast:
SQL syntax error near '' OR 1=1;--' at line 1
This exposed an injection vector that had not been caught by unit tests because the validation layer only checked for an @ symbol. The elderly persona, meanwhile, increased the system font scale to 200 % and triggered a layout overflow where the “Send” button moved off‑screen, making the flow impossible to complete without scrolling—a bug missed in manual testing because testers used the default font size.
The impatient persona’s rapid double‑tap on the submit button after the first request was still pending caused the app to queue two identical network calls. The backend, lacking idempotency checks, created two reset tokens, and the first email arrived while the second overwrote the token in SharedPreferences, leading to a confusing the user could not be used.
These findings were captured as video traces, and automatically generated Appium scripts that reproduced locally. The value validation ** debouncing the button.
Integrating Persona‑Driven Tests
- Upload APK to the SUSA CLI: susatest-agent run
run
expired before the user could use it, leading to a false‑negative recovery experience.
SUSA’s report highlighted these three defects, each with a short video clip, logcat excerpt, and the exact UI interaction sequence. The development team used the generated Appium script to add a regression test that:
- Sanitizes input (rejects strings containing SQL keywords).
- Constrains layout using
android:layout_weightand tests with multiple font scales. - Disables the submit button until the current network request finishes, with a visual progress indicator.
How to Leverage Autonomous Exploration in Your Workflow
- Nightly sanity: Schedule a SUSA run against the latest build candidate; treat any new persona‑generated failure as a high‑priority bug to investigate before the next release.
- Release gate: Require that the novice and accessibility personas achieve a ≥ 95 % success rate on core flows (login, signup, forgot‑password, checkout) before promoting to staging.
- Feedback loop: When SUSA creates a regression script, add it to your automated test suite (e.g., under
src/androidTest/java/com/example/tests/autonomous/). Over time, the suite grows with realistic, persona‑based cases that complement your hand‑written unit and Espresso tests.
Checklist and Takeaways
Quick Reference Checklist
| ✅ Item | Description |
|---|---|
| Happy path | Valid email/phone → success toast → reset link → password change → login with new credentials. |
| Error handling | Unregistered, malformed, empty inputs show inline validation; no account enumeration via timing or message differences. |
| Network resilience | Timeout, 5xx, 429 responses produce user‑friendly UI; no crash or stack trace leak. |
| Edge case inputs | Special characters, Unicode, leading/trailing spaces, clipboard paste, rotation, multi‑window, biometric fallback handled gracefully. |
| Accessibility | TalkBack navigation, sufficient contrast, ≥ 48 dp touch targets, error announcements, focus order logical. |
| Security | No tokens or PII in logs, no clear‑text traffic, tokens kept in memory only, rate‑limited, HTTPS enforced, App Links verified. |
| Localization | Test with at least one RTL language and one non‑Latin script; layout does not break. |
| Device states | Low storage, battery saver, doze mode, and background restrictions do not block the flow. |
| Observability | Capture logcat, network traces (via adb shell tcpdump or Stetho), and UI hierarchy on failure for rapid triage. |
| Automation | Maintain at least one Appium script for happy path, one Espresso suite for error paths, and a UIAutomator script for edge cases; run on every PR. |
| Persona testing | Periodically run an autonomous agent (e.g., SUSA) to discover hidden regressions; add its generated scripts to the regression suite. |
Core Takeaways
- Forgot‑password is a security‑sensitive recovery path; treat it with the same rigor as login or payment flows.
- Combine deterministic and exploratory testing: scripted suites guarantee regression coverage; persona‑driven agents uncover the “unknown unknowns” that only appear under atypical usage.
- **Automate the
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