How to Test Gift Cards on Android (Complete Guide)
Gift cards sit at the intersection of commerce, user trust, and regulatory compliance. When a user purchases or redeems a gift card inside an Android app, the flow touches payment gateways, credential
Why Gift Card Testing Matters on Android
Gift cards sit at the intersection of commerce, user trust, and regulatory compliance. When a user purchases or redeems a gift card inside an Android app, the flow touches payment gateways, credential storage, notification systems, and often third‑party APIs. A single failure—whether a silent decline, a mis‑routed refund, or an exposed card number—can lead to chargebacks, brand damage, or even legal penalties under PCI‑DSS or local consumer‑protection statutes.
On Android the attack surface widens because the OS permits side‑loading, background services, and a broad range of device configurations (different screen densities, Android versions, manufacturer skins). Gift‑card code frequently lives in a hybrid layer: native Java/Kotlin for UI, a React Native or Flutter bridge for cross‑platform logic, and a native C/C++ module for encryption. Each layer introduces its own failure modes that scripted tests often miss because they follow a single happy‑path scenario.
Testing gift cards therefore requires a matrix that covers functional correctness, error handling, accessibility, security, and device‑specific quirks. The following sections give you a complete, battle‑tested methodology that you can apply today, whether you are writing manual test cases, building automated suites, or leveraging autonomous exploration tools.
---
Core Gift Card Flow Overview
Before diving into test cases, it helps to map the typical end‑to‑end flow that most Android gift‑card implementations share. Understanding the touchpoints lets you decide where to inject faults, where to assert state, and where to monitor side effects.
- Entry Point – User navigates to a “Gift Cards” screen from a home tab, promo banner, or deep link.
- Card Selection / Creation – User chooses an existing card, enters a new card number and PIN, or opts to buy a new card via an in‑app purchase.
- Validation Request – The app sends the card details (often encrypted) to a backend validation endpoint. The service checks format, issuer BIN, and whether the card is active.
- Balance Inquiry – If validation passes, a separate call retrieves the current balance and any expiration date.
- Display – UI shows balance, card holder name, and optionally a barcode or QR code for in‑store redemption.
- Redemption Flow – User taps “Redeem”, enters a merchant code or scans a merchant QR, the app creates a transaction request, and the backend deducts the amount.
- Confirmation & Receipt – A success screen appears, a push notification or email receipt is sent, and the transaction is logged locally for audit.
- Error Handling – At any step, network errors, service errors, or invalid input trigger fallback UI (toast, snackbar, dialog) and may retry with exponential back‑off.
Each of these steps can be instrumented with logging, analytics, or test hooks. The matrix below expands on what to verify at each node.
---
Test Matrix for Gift Card Functionality
| Category | Sub‑category | Test Idea | Expected Result | Automation Hint | |
|---|---|---|---|---|---|
| Happy Path | New card purchase | User enters valid payment info, confirms purchase, receives card details | Card appears in list with correct balance, transaction ID logged | Espresso test with mocked payment gateway returning 200 | |
| Balance check | User opens card screen after purchase | Balance matches backend value, UI updates within 2 s | UI Automator scroll to card, assert TextView text | ||
| Redemption | User scans valid merchant QR, confirms deduction | Balance reduced by exact amount, receipt shown | Mock merchant API returning success, verify deduction | ||
| Error Paths | Invalid card number | User types 12‑digit number that fails Luhn check | Inline error appears, no network call | Espresso: type text, assert error TextView visibility | |
| Expired card | Use a test card with past expiry date | Dialog: “Card expired”, balance not shown | Mock backend returning 410 Gone | ||
| Network loss | Disable Wi‑Fi/cellular mid‑request | Toast: “Unable to connect”, retry button appears | Use Network Emulator (adb shell cmd network) | ||
| Server 500 | Backend returns internal error | Generic error dialog, option to retry later | Mock server with WireMock returning 500 | ||
| Edge Cases | Duplicate card entry | User attempts to add same card twice | Second entry blocked, toast: “Card already exists” | Check DB or shared prefs for duplicate entry | |
| Very long card number | Paste 30‑digit string | Input truncated or rejected per UI rule | Espresso: sendKeys long string, verify max length | ||
| Special characters in PIN | User enters “!@#” | PIN field rejects non‑numeric, shows hint | Validate input filter in unit test | ||
| Background app kill | System kills app while waiting for balance response | On relaunch, card shows loading spinner then correct data | Use adb shell am kill then relaunch, assert UI | ||
| Orientation change | Rotate device during redemption flow | UI preserves entered code, no data loss | Espresso: setLandscape, assert EditText text unchanged | ||
| Multi‑window | App runs in split‑screen while gift‑card screen open | Controls remain tappable, no overlapping UI | UI Automator: resize window, tap button | ||
| Accessibility | TalkBack navigation | User explores card list with TalkBack | Each item announces balance, card holder, actions | Accessibility Test Framework (ATF) assert spoken text | |
| Color contrast | Balance text on card background | Contrast ratio ≥ 4.5:1 (WCAG AA) | Use Android Studio’s Accessibility Scanner or manual check | ||
| Touch target size | Redeem button | Minimum 48 dp × 48 dp | Espresso: getBounds, assert width/height ≥ 48dp | ||
| Font scaling | User sets system font to 200 % | All text scales, no clipping | Change font size via settings, verify layout | ||
| Security / Privacy | Card data in logs | Perform a purchase, inspect logcat | No PAN or PIN appears in logs | `adb logcat | grep -i "card"` should return empty |
| Tokenization | Backend returns token instead of raw PAN | Token stored, never raw number | Mock backend returning token, verify DB stores token | ||
| Clipboard leakage | User copies card number to clipboard | Clipboard cleared after 30 s or on app exit | Use adb shell service call clipboard to read, assert cleared | ||
| Root detection | App runs on rooted device | App either blocks gift‑card use or shows warning | Use Magisk, verify behavior | ||
| Encryption at rest | Card details saved in SharedPreferences or DB | Data encrypted with AES‑256, key in Keystore | Extract file, attempt decryption without key | ||
| Performance | Cold start latency | Launch app from cleared state to gift‑card screen | < 2 s to show first card | Use adb shell am start -W and measure TotalTime | |
| List scrolling | 500 cards in list | Smooth 60 fps, no jank | UI Automator fling, monitor SurfaceFlinger frames | ||
| Battery impact | Repeated balance checks every 30 s for 10 min | < 2 % drain | Use adb shell dumpsys batterystats before/after | ||
| Localization | Right‑to‑left language | Switch device language to Arabic | Layout mirrors, text aligns right | Change locale, verify UI with adb shell setprop persist.sys.language ar | |
| Currency symbol | User locale set to Japan (JPY) | Balance shows ¥ symbol, correct decimal places | Change locale, assert TextView contains “¥” | ||
| Date format | Expiry date displayed | Matches locale’s short date format | Verify format pattern via SimpleDateFormat output |
*Table 1 – Comprehensive test matrix for Android gift‑card functionality. Each row can be turned into a manual test case or an automated assertion.*
---
Manual Testing Approach Step‑by‑Step
A disciplined manual session gives you confidence that the automated suite covers the right ground and catches usability quirks that scripts ignore. Follow this procedure on a physical device (or a well‑configured emulator) that matches your target market’s most common Android version and manufacturer skin.
1. Prepare the Test Environment
- Device: Choose a phone running Android 13 (API 33) with Google Play services. Keep a second device on Android 11 (API 30) to verify backward compatibility.
- App Build: Install the debuggable variant (
app-debug.apk) that enables test hooks (e.g.,BuildConfig.DEBUG). - Network: Use a Wi‑Fi network with a traffic‑shaping tool (e.g.,
clumsyon Windows ortcon Linux) to inject latency (200 ms) and packet loss (2 %). - Tooling: Have
adb,Android Studio Profiler, andAccessibility Scanner(If you need Scannerready. EnableDeveloper options → Show tapsandPointer location` for visual verification.
2. Baseline Sanity
- Launch the app from a cold start.
- Verify the home screen loads within the SLA (usually < 1.5 s).
- Navigate to the Gift Cards hub via the bottom nav or a deep link (
myapp://giftcards). - Confirm the screen title is announced by TalkBack and that the list (if any) is readable.
3. Happy‑Path Purchase
- Tap Add Gift Card → Buy New Card.
- Choose a denomination (e.g., $25).
- Fill in the test credit‑card fields provided by your sandbox gateway (Stripe test card
4242 4242 4242 4242, expiry12/34, CVC123). - Submit and watch for a success toast.
- Open the newly added card; ensure the balance reads $25.00, the card holder name matches the test user, and a barcode is rendered.
*Observation points*:
- Does the UI show a loading indicator while the payment request is in flight?
- Is there a fallback message if the sandbox returns a delay?
- Are analytics events (
gift_card_purchase_success) fired?
4. Error‑Path Injection
Repeat the purchase flow but alter one variable at a time:
| Variable | Invalid Value | Expected UI |
|---|---|---|
| Card number | 4242 4242 4242 4241 (fails Luhn) | Inline error: “Invalid card number” |
| Expiry month | 02 (past month) | Error: “Card expired” |
| CVC | 12 (too short) | Error: “CVC must be 3 digits” |
| Network | Disable Wi‑Fi after pressing Pay | Toast: “Unable to connect”, retry button appears |
| Server 500 | Use WireMock to return 500 | Dialog: “Something went wrong”, option to try again |
Mark each case as PASS if the UI matches the expectation and no crash or ANR occurs.
5. Edge‑Case Exploration
- Duplicate entry: Add the same card again; verify the app blocks it and shows a toast.
- Long input: Paste a 30‑character string into the card number field; confirm the field either truncates to the max length or shows a validation error.
- Orientation: While the balance request is in flight, rotate the device; ensure the spinner persists and the final balance displays correctly after rotation.
- Background kill: Issue
adb shell am killwhile waiting for a network response; relaunch the app and confirm the card appears with correct data (or shows a loading state if the request is still pending). - Multi‑window: Drag the gift‑card screen to the top half of a split‑screen; try to tap the Redeem button; ensure it responds and does not get obscured by the system divider.
6. Accessibility Checks
- Turn on TalkBack (
Settings → Accessibility → TalkBack). - Swipe to move focus across the gift‑card list; listen for each item’s description (should include balance, holder name, and actions).
- Open a card detail view; verify that the barcode image has a content‑description (e.g., “Barcode for ending in 1234”).
- Use the Accessibility Scanner (available from Play Store) to scan the screen; note any contrast failures or missing labels and fix them in the UI.
7. Security & Privacy Spot Checks
- Logcat inspection: Perform a purchase, then run
adb logcat -d | grep -i "card"; ensure no PAN, PIN, or OTP appears. - Clipboard: Copy the card number to clipboard, wait 30 seconds, then run
adb shell service call clipboard 2 i32 0to read the clipboard; it should return empty or a cleared value. - Root detection: Install Magisk, grant root, open the gift‑card screen; the app should either display a warning (“RootedDeviceDialog or block the purchase flow.
- Encryption at rest: Use
adb run-as(if applicable) and verify that the card number field is ciphertext, not plain text.cat shared_prefs/giftcards.xml
8. Performance & Battery
- Cold start: Clear recent apps, then run
adb shell am start -W com.example.app/.giftcard.ui.GiftCardActivity; note theTotalTimefield. - Jank test: Scroll a list of 100 cards quickly; use
adb shell dumpsys gfxinfo com.example.appto check for frames > 16 ms. - Battery: Start a script that triggers a balance request every 20 seconds for five minutes; capture
dumpsys batterystatsbefore and after; compute percent drain.
9. Localization Validation
- Switch device language to Hebrew (
iw) viaSettings → System → Languages & input → Languages. - Confirm the gift‑card screen mirrors (right‑to‑left layout) and that all text flows correctly.
- Change locale to Japan (
ja) and verify that the balance displays the yen symbol (¥) and that decimal separators follow Japanese convention (no decimal for yen).
10. Sign‑off
Create a checklist (see later section) and mark each item as PASS/FAIL. Any FAIL must be logged in your bug tracker with steps, device info, logcat snippet, and severity.
---
Automated Testing on Android
Manual validation is essential, but regression safety demands automated checks that run on every CI build. Below are the layers you should implement, with concrete code snippets that you can copy into your project.
Unit Tests – Business Logic
Test the view‑model or use‑case layer that handles gift‑card validation, balance calculation, and state transitions. Use JUnit5 and Mockito.
// GiftCardViewModelTest.kt
@ExperimentalCoroutinesApi
class GiftCardViewModelTest {
private val testDispatcher = UnconfinedTestDispatcher()
private lateinit var viewModel: GiftCardViewModel
private lateinit var repo: MockGiftCardRepository
@BeforeEach
fun setUp() {
Dispatchers.setMain(testDispatcher)
repo = MockGiftCardRepository()
viewModel = GiftCardViewModel(repo)
}
@AfterEach
fun tearDown() {
Dispatchers.resetMain()
}
@Test
fun `purchase success updates balance`() = runTest {
// Arrange
repo.purchaseResult = Result.Success(PurchaseResponse(cardId = "c1", balance = 2500))
// Act
viewModel.buyCard(denomination = 2500)
// Assert
assertEquals(viewModel.uiState.value.balance, 2500)
assertTrue(viewModel.uiState.value.isLoading == false)
}
@Test
fun `invalid card number shows error`() = runTest {
repo.purchaseResult = Result.Failure(InvalidCardException())
viewModel.buyCard(denomination = 2500, cardNumber = "123")
assertEquals(viewModel.uiState.value.error, R.string.error_invalid_card)
}
}
*Key points*:
- Use
UnconfinedTestDispatcherto avoid timing issues with coroutines. - Mock the repository to isolate network logic.
- Assert both UI state (loading, error, data) and side‑effects (analytics calls if you expose them).
Instrumented UI Tests – Espresso
Espresso shines for validating UI interactions on the main thread. Pair it with IdlingResource to wait for network calls if you use OkHttp’s IdlingResource.
// GiftCardPurchaseTest.kt
@RunWith(AndroidJUnit4::class)
class GiftCardPurchaseTest {
@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class)
@Before
fun setUp() {
// Enqueue mocked responses via MockWebServer
MockWebServerUtil.enqueue(
path = "/api/giftcards/buy",
json = """{"cardId":"c123","balance":2500}""",
responseCode = 200
)
}
@Test
fun userCanBuyGiftCardAndSeeBalance() {
// Navigate to gift‑card screen
onView(withId(R.id.nav_gift_cards)).perform(click())
// Click “Buy New Card”
onView(withText("Buy New Card")).perform(click())
// Fill form
onView(withId(R.id.card_number_edit))
.perform(replaceText("4242 4242 4242 4242"), closeSoftKeyboard())
onView(withId(R.id.expiry_edit))
.perform(replaceText("12/34"), closeSoftKeyboard())
onView(withId(R.id.cvc_edit))
.perform(replaceText("123"), closeSoftKeyboard())
// Submit
onView(withId(R.id.btn_purchase)).perform(click())
// Verify loading spinner disappears
onView(withId(R.id.progress_bar)).check(matches(not(isDisplayed())))
// Assert balance displayed
onView(withId(R.id.balance_text))
.check(matches(withText(containsString("$25.00"))))
}
}
*Notes*:
- Replace
MockWebServerUtilwith your own helper that uses OkHttp’sMockWebServer. - The test asserts that the loading indicator is gone before checking the balance, preventing flaky checks due to timing.
UI Automator – System‑Level Scenarios
Use UI Automator for scenarios that cross app boundaries (e.g., sharing a gift‑card via Android Share Sheet, or testing split‑screen behavior).
// GiftCardShareTest.java
@RunWith(AndroidJUnit4.class)
public class GiftCardShareTest {
private static final String PACKAGE_NAME = "com.example.app";
@Before
public void pushHome() throws Exception {
UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
device.pressHome();
}
@Test
public void shareGiftCardViaBluetooth() {
// Launch app directly to gift‑card detail
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
Intent intent = new Intent(context, GiftCardDetailActivity.class);
intent.putExtra("cardId", "c123");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
// Wait for barcode to appear
UiObject barcode = device.findObject(new UiSelector()
.resourceId(PACKAGE_NAME + ":id/barcode_image"));
assertTrue(barcode.waitForExists(5000));
// Tap share button
UiObject shareBtn = device.findObject(new UiSelector()
.resourceId(PACKAGE_NAME + ":id/share_btn"));
shareBtn.click();
// Choose Bluetooth from share sheet
UiObject btOption = device.findObject(new UiSelector()
.text("Bluetooth"));
assertTrue(btOption.waitForExists(5000));
btOption.click();
// Verify Bluetooth picker appears
UiObject btPicker = device.findObject(new UiSelector()
.className("android.widget.ListView"));
assertTrue(btPicker.waitForExists(5000));
}
}
This test confirms that the app correctly prepares a share intent and that the system share sheet lists Bluetooth as an option.
Automated Accessibility Tests
Integrate the Accessibility Test Framework (ATF) from Google into your unit test suite.
// GiftCardAccessibilityTest.kt
@RunWith(AndroidJUnit4::class)
class GiftCardAccessibilityTest {
@get:Rule
val activityRule = ActivityScenarioRule(GiftCardListActivity::class)
@Test
public void giftCardListHasNoAccessibilityIssues() {
onView(withId(R.id.recycler_view)).check(
matches(isDisplayed()))
AccessibilityChecks.check()
}
}
Run this as part of your unit test suite; any WCAG violation will cause the test to fail, providing a fast feedback loop.
Performance Testing with Macrobenchmark
The Macrobenchmark library lets you measure cold start, scroll jank, and frame timing on a real device or emulator.
// GiftCardStartupBenchmark.kt
@LargeTest
@RunWith(AndroidJUnit4::class)
class GiftCardStartupBenchmark {
@get:Rule
val benchmarkRule = MacrobenchmarkRule()
@Test
fun coldStart() = benchmarkRule.measureRepeated(
packageName = "com.example.app",
metrics = listOf(StartupTimingMetric()),
iterations = 5,
startupMode = StartupMode.COLD
) {
// Press home to ensure a clean state
pressHome()
// Launch the gift‑card activity directly
startActivityAndWait()
}
}
The output includes TimeToInitialDisplay and TimeToFullDraw; compare against your SLA (e.g., < 1500 ms for initial display).
---
Tooling and Frameworks Specific to Gift Card Testing
| Tool / Framework | Primary Use | Why It Helps Gift‑Card Tests | Setup Hint |
|---|---|---|---|
| MockWebServer (OkHttp) | Simulate backend endpoints | Allows you to inject success, error, latency, and malformed JSON without touching real services | Add implementation("com.squareup.okhttp3:mockwebserver:4.12.0"); enqueue responses in @Before |
| WireMock | Stand‑alone HTTP mock with DSL | Great for complex scenario testing (e.g., conditional responses based on headers) | Run as Docker container; point app’s base URL to http://localhost:8080 |
| Firebase Test Lab | Run instrumentation tests on a matrix of real devices | Catch device‑specific bugs (e.g., Samsung OnePlus UI quirks) | Use gcloud firebase test android run --type instrumentation --app app-debug.apk --test app-test.apk --device model=Pixel3,version=33 |
| LeakCanary | Detect memory leaks | Gift‑card screens often hold bitmaps (barcodes) that can leak if not cleared | Add debugImplementation("com.squareup.leakcanary:leakcanary-android:2.13") |
| Stetho | Inspect network, DB, SharedPreferences from Chrome DevTools | Quickly verify that card data is not stored in plain text | Add debugImplementation("com.facebook.stetho:stetho:1.6.0") and init in Application.onCreate() |
| Android Studio Profiler | CPU, memory, network, energy | Spot performance regressions during balance polls or barcode generation | Use View → Tool Windows → Profiler while running a test scenario |
| Accessibility Scanner | Automated WCAG checks | Detect missing content‑descriptions, low contrast, small touch targets | Install from Play Store, run on device, review suggestions |
| MobSF (Mobile Security Framework) | Static/dynamic analysis for security flaws | Can flag hard‑coded keys, insecure logging, or improper export of activities | Upload APK, review the “Insecure Data Storage” and “Improper Certificate Validation” sections |
---
Edge Cases That Appear Only in Production
Even the most exhaustive lab matrix can miss issues that only surface when the app runs in the wild. Below are the production‑only phenomena that have historically broken gift‑card flows on Android, together with detection strategies.
1. Intermittent Network Conditions
- Problem: Users on moving trains or in elevators experience brief total loss followed by rapid recovery. If your app does not handle
IOExceptiongracefully, the UI may stay stuck in a loading state. - Detection: Use the Network Emulator (
adb shell cmd network) to simulate LTE with 30 % packet loss and 500 ms latency, then run a loop of purchase attempts. Watch for UI freeze or duplicate network calls.
2. Background Service Conflicts
- Problem: Some OEMs aggressively kill background services to save battery. If your balance‑refresh logic lives in a
JobIntentServicethat gets killed, the card may show stale balance after the user returns to the app. - Detection: Enable Don’t keep activities in Developer options, then repeatedly navigate away and back while monitoring LiveData or Room queries for updates.
3. Rooted or Custom ROM Devices
- Problem: Certain custom ROMs modify the behavior of
KeyStoreorSecureRandom, causing encryption failures that are invisible on stock builds. - Detection: Test on a device running LineageOS or with Magisk installed. Verify that any cryptographic operation (e.g., generating a token for the backend) returns success and that the resulting token is accepted by the server.
4. Android Version‑Specific UI Glitches
- Problem: On Android 12 (API 31) the ripple effect changed; custom views that hard‑code
android:foregroundmay cause the gift‑card button to be unresponsive in certain themes. - Detection: Use the UI Automator to perform a tap on the gift‑card button across API 21‑33 on a device farm; assert that the click listener fires (e.g., via a
CountingIdlingResource).
5. Battery‑Optimization Whitelisting
- Problem: If your app relies on a periodic
WorkManagerto refresh gift‑card balances, aggressive battery optimization may defer the work indefinitely, leading to outdated balances shown to the user. - Detection: Go to
Settings → Apps → YourApp → Battery → Battery optimizationand set it to “Optimize”. Then trigger a balance refresh and verify whether the work runs within the expected window (useadb shell cmd jobscheduler run).
6. Multi‑User / Guest Sessions
- Problem: On tablets or shared devices, a secondary user may have limited permissions to write to external storage, causing barcode generation to fail silently.
- Detection: Create a second user (
Settings → System → Multiple users → Add user), switch to that user, attempt to generate and share a gift‑card barcode, and confirm the image is saved or shared correctly.
7. Locale‑Specific Formatting Bugs
- Problem: Some languages use non‑ASCII digits (e.g., Arabic-Indic digits). If you parse card numbers with
Integer.parseInt()without specifyingLocale.US, the conversion fails. - Detection: Change device language to Arabic, attempt to add a card, and observe whether the input is rejected or corrupted.
8. Push Notification Interference
- Problem: A heads‑up notification that arrives while the user is entering a PIN can cause the soft keyboard to dismiss, losing focus and leading to incomplete data submission.
- Detection: Use
adb shell cmd notification post -Swhile the PIN field has focus; verify that the field retains focus and the user can continue typing.--title "Test" --text "Hi"
9. SD‑Card Adoptable Storage
- Problem: On devices with adoptable storage, the app’s internal files may be moved to the encrypted SD card. If you mistakenly rely on
getExternalFilesDir()for storing temporary token files, the file may become inaccessible after a reboot. - Detection: Enable adoptable storage, move app data to the SD card, reboot, then attempt a gift‑card purchase and check for any
FileNotFoundException.
10. Concurrent Gift‑Card Operations
- Problem: Power users may open two instances of the gift‑card screen (via split‑screen or recent‑apps) and try to redeem the same card simultaneously, causing a race condition on the backend.
- Detection: Launch two instances via UI Automator, initiate redemption in both within 200 ms, and verify that the backend only accepts one request and returns a clear error for the duplicate.
---
Accessibility and WCAG Checks for Gift Card UI
Ensuring that gift‑card flows are usable by people with disabilities is not only a legal requirement in many jurisdictions but also expands your addressable market. Below are concrete checks you should automate or include in your manual test plan.
1. TalkBack Compatibility
- Rule: Every interactive element must have a meaningful content‑description.
- Test: With TalkBack enabled, swipe left/right across the gift‑card list; each item should announce something like “Gift card ending in 1234, balance $25.00, button, redeem”.
- Automation: Use the Accessibility Test Framework (
AccessibilityChecks.check()) in an instrumented test to verify that no view fails thecontentDescriptioncheck.
2. Color Contrast
- Rule: Text and icons must have a contrast ratio of at least 4.5:1 against their background (AA) or 7:1 (AAA).
- Test: Use the Accessibility Scanner or the Contrast Checker in Android Studio to scan the gift‑card detail screen. Pay special attention to the balance text over a gradient background and the barcode’s quiet zones.
- Fix: If contrast fails, adjust the text color or add a semi‑transparent backing shape behind the text.
3. Touch Target Size
- Rule: Minimum 48 dp × 48 dp for tappable elements.
- Test: Enable
Show layout boundsin Developer options; visually inspect the redeem button, the “Add Card” button, and any icon‑only controls. - Automation: In an Espresso test, retrieve the view’s bounds and assert
width >= 48dp && height >= 48dp.
4. Text Scaling
- Rule: UI must remain legible and not clipped when the user sets font size to Large or Largest (or enables “Bold text”).
- Test: Go to
Settings → Accessibility → Font sizeand set to Largest; navigate through the gift‑card flow and confirm that all text is fully visible, buttons do not overlap, and scrollable areas still scroll. - Automation: Change the configuration via
ConfigurationCompat.setFontScale(configuration, 2.0f)in a UI Automator test and assert that no view’sgetHeight()returns zero.
5. Focus Order
- Rule: When navigating with a keyboard or D‑pad, focus should move in a logical, predictable order (usually left‑to‑right, top‑to‑bottom).
- Test: Connect a USB‑OTG keyboard, tab through the gift‑card screen; ensure focus lands on the card number field first, then expiry, then CVC, then the purchase button.
- Automation: Use
UiDevice.waitForIdle()after eachpressKey(KeyEvent.KEYCODE_DPAD_DOWN)and assert that the expected view has focus (view.isFocused()).
6. Error Announcement
- Rule: Validation errors must be announced immediately
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