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

March 08, 2026 · 18 min read · How-To Guides

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.

  1. Entry Point – User navigates to a “Gift Cards” screen from a home tab, promo banner, or deep link.
  2. 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.
  3. 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.
  4. Balance Inquiry – If validation passes, a separate call retrieves the current balance and any expiration date.
  5. Display – UI shows balance, card holder name, and optionally a barcode or QR code for in‑store redemption.
  6. 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.
  7. Confirmation & Receipt – A success screen appears, a push notification or email receipt is sent, and the transaction is logged locally for audit.
  8. 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

CategorySub‑categoryTest IdeaExpected ResultAutomation Hint
Happy PathNew card purchaseUser enters valid payment info, confirms purchase, receives card detailsCard appears in list with correct balance, transaction ID loggedEspresso test with mocked payment gateway returning 200
Balance checkUser opens card screen after purchaseBalance matches backend value, UI updates within 2 sUI Automator scroll to card, assert TextView text
RedemptionUser scans valid merchant QR, confirms deductionBalance reduced by exact amount, receipt shownMock merchant API returning success, verify deduction
Error PathsInvalid card numberUser types 12‑digit number that fails Luhn checkInline error appears, no network callEspresso: type text, assert error TextView visibility
Expired cardUse a test card with past expiry dateDialog: “Card expired”, balance not shownMock backend returning 410 Gone
Network lossDisable Wi‑Fi/cellular mid‑requestToast: “Unable to connect”, retry button appearsUse Network Emulator (adb shell cmd network)
Server 500Backend returns internal errorGeneric error dialog, option to retry laterMock server with WireMock returning 500
Edge CasesDuplicate card entryUser attempts to add same card twiceSecond entry blocked, toast: “Card already exists”Check DB or shared prefs for duplicate entry
Very long card numberPaste 30‑digit stringInput truncated or rejected per UI ruleEspresso: sendKeys long string, verify max length
Special characters in PINUser enters “!@#”PIN field rejects non‑numeric, shows hintValidate input filter in unit test
Background app killSystem kills app while waiting for balance responseOn relaunch, card shows loading spinner then correct dataUse adb shell am kill then relaunch, assert UI
Orientation changeRotate device during redemption flowUI preserves entered code, no data lossEspresso: setLandscape, assert EditText text unchanged
Multi‑windowApp runs in split‑screen while gift‑card screen openControls remain tappable, no overlapping UIUI Automator: resize window, tap button
AccessibilityTalkBack navigationUser explores card list with TalkBackEach item announces balance, card holder, actionsAccessibility Test Framework (ATF) assert spoken text
Color contrastBalance text on card backgroundContrast ratio ≥ 4.5:1 (WCAG AA)Use Android Studio’s Accessibility Scanner or manual check
Touch target sizeRedeem buttonMinimum 48 dp × 48 dpEspresso: getBounds, assert width/height ≥ 48dp
Font scalingUser sets system font to 200 %All text scales, no clippingChange font size via settings, verify layout
Security / PrivacyCard data in logsPerform a purchase, inspect logcatNo PAN or PIN appears in logs`adb logcatgrep -i "card"` should return empty
TokenizationBackend returns token instead of raw PANToken stored, never raw numberMock backend returning token, verify DB stores token
Clipboard leakageUser copies card number to clipboardClipboard cleared after 30 s or on app exitUse adb shell service call clipboard to read, assert cleared
Root detectionApp runs on rooted deviceApp either blocks gift‑card use or shows warningUse Magisk, verify behavior
Encryption at restCard details saved in SharedPreferences or DBData encrypted with AES‑256, key in KeystoreExtract file, attempt decryption without key
PerformanceCold start latencyLaunch app from cleared state to gift‑card screen< 2 s to show first cardUse adb shell am start -W and measure TotalTime
List scrolling500 cards in listSmooth 60 fps, no jankUI Automator fling, monitor SurfaceFlinger frames
Battery impactRepeated balance checks every 30 s for 10 min< 2 % drainUse adb shell dumpsys batterystats before/after
LocalizationRight‑to‑left languageSwitch device language to ArabicLayout mirrors, text aligns rightChange locale, verify UI with adb shell setprop persist.sys.language ar
Currency symbolUser locale set to Japan (JPY)Balance shows ¥ symbol, correct decimal placesChange locale, assert TextView contains “¥”
Date formatExpiry date displayedMatches locale’s short date formatVerify 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

2. Baseline Sanity

  1. Launch the app from a cold start.
  2. Verify the home screen loads within the SLA (usually < 1.5 s).
  3. Navigate to the Gift Cards hub via the bottom nav or a deep link (myapp://giftcards).
  4. Confirm the screen title is announced by TalkBack and that the list (if any) is readable.

3. Happy‑Path Purchase

*Observation points*:

4. Error‑Path Injection

Repeat the purchase flow but alter one variable at a time:

VariableInvalid ValueExpected UI
Card number4242 4242 4242 4241 (fails Luhn)Inline error: “Invalid card number”
Expiry month02 (past month)Error: “Card expired”
CVC12 (too short)Error: “CVC must be 3 digits”
NetworkDisable Wi‑Fi after pressing PayToast: “Unable to connect”, retry button appears
Server 500Use WireMock to return 500Dialog: “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

6. Accessibility Checks

  1. Turn on TalkBack (Settings → Accessibility → TalkBack).
  2. Swipe to move focus across the gift‑card list; listen for each item’s description (should include balance, holder name, and actions).
  3. Open a card detail view; verify that the barcode image has a content‑description (e.g., “Barcode for ending in 1234”).
  4. 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

8. Performance & Battery

9. Localization Validation

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*:

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*:

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 / FrameworkPrimary UseWhy It Helps Gift‑Card TestsSetup Hint
MockWebServer (OkHttp)Simulate backend endpointsAllows you to inject success, error, latency, and malformed JSON without touching real servicesAdd implementation("com.squareup.okhttp3:mockwebserver:4.12.0"); enqueue responses in @Before
WireMockStand‑alone HTTP mock with DSLGreat 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 LabRun instrumentation tests on a matrix of real devicesCatch 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
LeakCanaryDetect memory leaksGift‑card screens often hold bitmaps (barcodes) that can leak if not clearedAdd debugImplementation("com.squareup.leakcanary:leakcanary-android:2.13")
StethoInspect network, DB, SharedPreferences from Chrome DevToolsQuickly verify that card data is not stored in plain textAdd debugImplementation("com.facebook.stetho:stetho:1.6.0") and init in Application.onCreate()
Android Studio ProfilerCPU, memory, network, energySpot performance regressions during balance polls or barcode generationUse View → Tool Windows → Profiler while running a test scenario
Accessibility ScannerAutomated WCAG checksDetect missing content‑descriptions, low contrast, small touch targetsInstall from Play Store, run on device, review suggestions
MobSF (Mobile Security Framework)Static/dynamic analysis for security flawsCan flag hard‑coded keys, insecure logging, or improper export of activitiesUpload 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

2. Background Service Conflicts

3. Rooted or Custom ROM Devices

4. Android Version‑Specific UI Glitches

5. Battery‑Optimization Whitelisting

6. Multi‑User / Guest Sessions

7. Locale‑Specific Formatting Bugs

8. Push Notification Interference

9. SD‑Card Adoptable Storage

10. Concurrent Gift‑Card Operations

---

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

2. Color Contrast

3. Touch Target Size

4. Text Scaling

5. Focus Order

6. Error Announcement

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