How to Test Data Export on Android (Complete Guide)
Data export is a feature that lets users pull information out of an app—contacts, logs, reports, media, or any structured payload—and save it locally or share it via another app. When export works, us
Why Data Export Matters on Android
Data export is a feature that lets users pull information out of an app—contacts, logs, reports, media, or any structured payload—and save it locally or share it via another app. When export works, users retain control of their data, can back it up, migrate to a new device, or comply with regulations such as GDPR. When it fails, the consequences are immediate: users lose trust, support tickets spike, and regulatory auditors may flag non‑compliance.
On Android, export often touches several system components: the Storage Access Framework (SAF), Intent‑based sharing, FileProvider, and sometimes custom content providers. Each of these layers introduces failure points that are not exercised by typical UI‑only tests. A button may appear enabled, yet the underlying Intent fails because the target app does not support the MIME type, or the app lacks the WRITE_EXTERNAL_STORAGE permission on legacy devices. Because export is frequently a “fire‑and‑forget” action, bugs hide until a real user tries to save a large file or shares with an unfamiliar app, at which point the crash or silent failure appears in production.
Testing export therefore requires a matrix that goes beyond “tap the share button and see if a chooser appears.” You must verify that the generated file is correct, that it lands in the expected location, that the app handles interruptions (low storage, revoked permission), and that the export respects accessibility and security constraints. The following sections break down a practical approach to cover all of these angles.
Common Failure Modes in Production
Understanding what breaks in the wild helps you prioritize test cases. Below are the most frequent categories observed in Android apps that offer export:
| Failure Category | Typical Symptom | Root Cause |
|---|---|---|
| Permission loss | Export button does nothing or throws SecurityException on Android 10+ | App requests legacy WRITE_EXTERNAL_STORAGE but runtime permission denied; scoped storage not handled |
| Incorrect MIME type | Chooser shows “No apps can perform this action” or opens wrong app | Intent.setType() uses generic */* or wrong subtype; receiving app filters on specific MIME |
| FileProvider misconfiguration | FileUriExposedException or NullPointerException when sharing | provider_paths.xml missing the exported directory, or authority mismatch |
| Large file handling | App crashes with OutOfMemoryError or produces truncated file | Export writes entire dataset into memory before streaming to disk |
| Interrupted write | Partial file left behind, user sees corrupted export | No cleanup on IOException; no transactional write (write to temp then rename) |
| Accessibility breakdown | TalkBack does not announce export success/failure | Missing contentDescription on button, no toast or accessibility announcement |
| Security leakage | Exported file world‑readable, contains sensitive data not intended for export | File created with MODE_WORLD_READABLE or stored in external cache without encryption |
| Intent resolution failure | Chooser appears empty on some OEM skins | OEM replaces default chooser with a custom one that filters by activity name patterns |
Each of these categories can be reproduced with a targeted test matrix, which we define next.
Test Matrix for Data Export
A comprehensive matrix covers the happy path, error paths, edge cases, accessibility, and security/privacy. Use it as a checklist when writing manual test cases or automated scenarios.
| Test ID | Description | Preconditions | Steps | Expected Result | Pass/Fail Criteria |
|---|---|---|---|---|---|
| EX‑01 | Export small dataset via Share button | App logged in, at least one exportable item | Tap Share → Choose “Save to Files” → Pick a folder → Confirm | File appears in chosen folder, contents match source data, no crash | File size >0, MD5 matches source, toast shows success |
| EX‑02 | Export large dataset (≥10 MB) | Same as EX‑01, but enable “Export all logs” option | Same steps as EX‑01 | File created, no OOM, progress indicator shown | File size ≈ source size, memory usage stays <80 MB, no crash |
| EX‑03 | Export with revoked WRITE_EXTERNAL_STORAGE (Android 9‑) | Grant permission, then revoke via Settings → Apps → YourApp → Permissions → Storage | Attempt export | Export fails gracefully, shows permission rationale dialog | No crash, dialog appears, button re‑enables after granting |
| EX‑04 | Export with scoped storage denied (Android 10+) | Do not request MANAGE_EXTERNAL_STORAGE, rely on SAF | Tap Share → Choose “Save to Files” → Attempt to write to a restricted folder (e.g., root of internal storage) | Chooser blocks the folder, or app receives ActivityResult.RESULT_CANCELED | No crash, appropriate error message shown |
| EX‑05 | Incorrect MIME type (e.g., exporting PDF as text/plain) | Export PDF report | Trigger export, observe chooser | Chooser lists only apps that accept text/plain (likely none) → “No apps can perform this action” | Test expects correct MIME (application/pdf) and chooser shows PDF viewers |
| EX‑06 | FileProvider misconfiguration | Export image, provider_paths.xml missing | Trigger export | FileUriExposedException logged, crash | Test expects no exception, file shared correctly |
| EX‑07 | Interrupted write (low storage) | Fill device storage to <5 MB free | Attempt export of medium‑sized file | Export fails, partial file removed, user notified | No leftover file, error dialog shown, app state stable |
| EX‑08 | Accessibility: TalkBack announcement | TalkBack enabled | Perform export | TalkBack announces “Export started”, then “Export succeeded” or “Export failed” | Announcements present, no silence |
| EX‑09 | TalkBack: button description missing | TalkBack enabled, button lacks contentDescription | Navigate to export button | TalkBack reads ambiguous label (e.g., “button”) | Test expects meaningful description like “Export report” |
| EX‑10 | Security: world‑readable file | Export file, check its mode | After export, run adb shell ls -l /path/to/file | File mode -rw-r--r-- (owner read/write, group/others read) or stricter | Expect -rw------- (private) or encrypted; world‑readable fails |
| EX‑11 | Export contains unintended PII | Export includes user email, token | Inspect exported file (e.g., via adb pull and cat) | No email/auth token present unless explicitly allowed | If present, test fails (data leakage) |
| EX‑12 | Chooser empty on OEM skin (e.g., Xiaomi) | Device with MIUI, export PDF | Trigger export | Chooser shows at least one PDF viewer | If chooser empty, test fails (needs implicit intent fallback) |
| EX‑13 | Export after app upgrade (data migration) | Install v1, export data, upgrade to v2, export again | Compare two exports | Both exports readable, schema version handled | No corruption, version field updated |
| EX‑14 | Export cancellation via back button | Export in progress, press Back | Export operation aborts cleanly | No crash, temporary file removed, UI returns to prior state | Test expects no leftover file, UI responsive |
| EX‑15 | Export with custom file name containing Unicode | File name “报告_٢٠٢٤.pdf” | Trigger export, specify name | File saved with correct Unicode name, accessible via file manager | Name appears correctly, no garbled characters |
You can expand this matrix with additional rows for specific export targets (email attachment, Bluetooth, NFC) or for particular data types (CSV, JSON, SQLite dump). The key is to pair each scenario with a clear pass/fail criterion that can be automated or manually verified.
Manual Testing Approach (Step‑by‑Step)
Even when you plan to automate, a manual exploratory pass uncovers assumptions that automated scripts miss. Follow this procedure on a representative device (or a set of devices covering different Android versions and OEM skins).
- Prepare the test environment
- Install the app under test from a debuggable build.
- Enable Developer options → USB debugging.
- Grant any runtime permissions the app requests (location, contacts, etc.) so that export has source data.
- Clear the app’s data (
adb shell pm clear com.example.app) to start from a clean state.
- Verify the export entry point
- Locate every UI element that can start an export (share button, menu item, swipe gesture).
- For each, note its contentDescription, enabled state, and visual contrast (use Android Studio’s Layout Inspector or the Accessibility Scanner).
- Happy‑path execution
- Tap the export element.
- If a chooser appears, select a known destination (e.g., “Save to Files” → Documents folder).
- Confirm the operation (some apps require an extra “OK”).
- Wait for a toast or snackbar indicating success.
- Post‑export validation
- Use
adb shellto locate the file:
adb shell run-as com.example.app ls -l /data/data/com.example.app/files/export/
or, if the app uses SAF, pull the file via adb shell content read --uri content://....
- Check file size >0.
- Compute a hash (
adb shell sha256sum /path/to/file) and compare to a known good hash generated from the source data (you can compute this offline). - Open the file with the appropriate viewer (PDF, CSV, etc.) to confirm readability.
- Error‑path injection
- Permission revocation: Go to Settings → Apps → YourApp → Permissions → Storage → Deny, then repeat step 3. Observe whether the app shows a rationale dialog and does not crash.
- Low storage: Use
adb shell sm set-disksto emulate full storage on a rooted device, or fill the disk with large files viaadb push. Then attempt export and verify graceful failure. - Interrupted write: While export is in progress, quickly toggle airplane mode or unplug USB (if writing to external storage) to simulate an I/O error. Confirm the app removes any partial output.
- Accessibility checks
- Enable TalkBack (
Settings → Accessibility → TalkBack). - Navigate to the export button; verify it announces a meaningful label.
- Trigger export; listen for start and completion announcements.
- Use the Accessibility Scanner app to flag any missing contentDescriptions or low‑contrast elements.
- Security and privacy audit
- After export, inspect the file’s mode:
adb shell ls -l /path/to/exported/file
Ensure it is not world‑readable (-rw-r--r-- is acceptable only if the file contains no sensitive data).
- Pull the file and search for patterns that should not appear (e.g.,
\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z]{2,}\bfor email addresses). Usegrepon your workstation. - If the app encrypts exports, verify that the file is not plaintext (e.g., check for AES header or use
filecommand).
- Regression check across devices
- Repeat steps 3‑7 on at least three devices:
- A Google Pixel running the latest Android version.
- A Samsung device with One UI (often modifies the chooser).
- A budget device running Android Go or an OEM skin with aggressive battery optimizations.
- Document findings
- For each test case, record: device model, Android version, steps taken, observed behavior, logs (
adb logcat), and any screenshots or video captures. - Tag issues as “blocker”, “major”, or “minor” based on impact (crash, data loss, regulatory risk).
By following this manual routine, you create a reproducible baseline that can later be translated into automated test scripts. The next section shows how to do that efficiently on Android.
Automated Testing on Android
Automation speeds up regression and enables continuous integration. Android offers several frameworks that can interact with the export flow: Espresso for UI assertions, UIAutomator for cross‑app interactions (chooser, file picker), and ADB‑based scripts for file verification and permission manipulation.
Setting up the test project
Add the following dependencies to your build.gradle (Module: app) if you are using AndroidJUnitRunner:
dependencies {
androidTestImplementation "androidx.test:core:1.5.0"
androidTestImplementation "androidx.test.ext:junit:1.1.5"
androidTestImplementation "androidx.test.espresso:espresso-core:3.5.1"
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.2.0"
testImplementation "junit:junit:4.13.2"
}
Create a test class under src/androidTest/java/com/example/app/export/ExportTest.kt.
Helper utilities
object ExportHelper {
fun grantStoragePermission() {
// For Android 9 and below
val pm = InstrumentationRegistry.getInstrumentation().targetContext.packageManager
pm.grantPermission(
InstrumentationRegistry.getInstrumentation().targetContext.packageName,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
}
fun revokeStoragePermission() {
val pm = InstrumentationRegistry.getInstrumentation().targetContext.packageManager
pm.revokePermission(
InstrumentationRegistry.getInstrumentation().targetContext.packageName,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
}
fun waitForChooser(timeoutMillis: Long = 5000): UiObject2 {
val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
return uiDevice.wait(
Until.findObject(By.text("Complete action using")),
timeoutMillis
)!!
}
fun pickFirstChooserOption() {
val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
val first = uiDevice.findObject(By.clazz("android.widget.CheckedTextView"))
?: throw AssertionError("No chooser options")
first.click()
}
fun exportSucceededToast(): Matcher<View> {
return withText(containsString("Export succeeded"))
.inRoot(isDialog())
}
fun getExportedFilePath(): String? {
// Adjust according to your app's storage strategy
val context = InstrumentationRegistry.getInstrumentation().targetContext
val file = File(context.filesDir, "export/report.pdf")
return if (file.exists()) file.absolutePath else null
}
}
Happy‑path Espresso test
@RunWith(AndroidJUnit4::class)
class ExportTest {
@Before
fun setUp() {
// Ensure app is in a known state (e.g., logged in, has data)
ExportHelper.grantStoragePermission()
// Navigate to screen with export button – replace with your actual navigation
onView(withId(R.id.nav_export)).perform(click())
}
@Test
fun exportSmallFile_shareViaSAF() {
// Trigger export
onView(withId(R.id.btn_export)).perform(click())
// Handle chooser – choose "Documents" (SAF)
onView(withText("Documents")).perform(click())
// Confirm folder selection if required
onView(withId(android:id/button1)).perform(click())
// Verify success toast
onView(ExportHelper.exportSucceededToast()).inRoot(isToast()).check(matches(isDisplayed()))
// Verify file exists and has expected content
val path = ExportHelper.getExportedFilePath()
assertNotNull(path)
val file = File(path!!)
assertTrue(file.length() > 0L)
// Example: check first line of CSV
val firstLine = file.bufferedReader().use { it.readLine() }
assertEquals("id,name,value", firstLine)
}
}
Error‑path test (permission denied) using UIAutomator
@Test
fun exportWithDeniedPermission_showsRationale() {
ExportHelper.revokeStoragePermission()
onView(withId(R.id.btn_export)).perform(click())
// Expect a dialog explaining why storage is needed
onView(withText("Allow access to storage?"))
.check(matches(isDisplayed()))
onView(withId(android:id/button1)) // Allow
.perform(click())
// After granting, export should proceed
onView(withId(R.id.btn_export)).perform(click())
onView(ExportHelper.exportSucceededToast()).inRoot(isToast()).check(matches(isDisplayed()))
}
Large‑file export and memory check
You can use Espresso’s IdlingResource to wait for a background export service, then inspect memory via adb shell dumpsys meminfo. A simple approach is to add a test rule that fails if the process’s PSS exceeds a threshold:
@Rule
@JvmField
val memoryRule = MemoryThresholdRule(maxPssMb = 120)
class MemoryThresholdRule(
private val maxPssMb: Int
) : TestWatcher() {
override fun finished(description: Description?) {
val pid = android.os.Process.myPid()
val memInfo = runShellCommand("dumpsys meminfo $pid")
val pssLine = memInfo.lines()
.firstOrNull { it.contains("Total PSS") }
?: throw AssertionError("Could not read PSS")
val pssKb = pssLine.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.last()
val pssMb = (pssKb.toInt() / 1024.0)
if (pssMb > maxPssMb) {
throw AssertionError("PSS exceeded limit: $pssMb MB > $maxPssMb MB")
}
}
private fun runShellCommand(cmd: String): String {
val process = Runtime.getRuntime().exec("su -c $cmd")
return process.inputStream.bufferedReader().readText()
}
}
Verifying file integrity with a separate verification step
After the UI test finishes, you can pull the file and run a checksum comparison in the same test (still on the instrumentation thread, but using adb via ProcessBuilder):
@Test
fun exportedFileMatchesSource() {
// ... perform export as before ...
val localPath = ExportHelper.getExportedFilePath()!!
// Pull to host
val pullCmd = "adb -s ${InstrumentationRegistry.getInstrumentation().uiDevice.adbId} pull $localPath /tmp/exported.pdf"
ProcessBuilder("bash", "-c", pullCmd).inheritIO().start().waitFor()
// Compute hash on host (you could also compute on device)
val hashCmd = "sha256sum /tmp/exported.pdf"
val hash = ProcessBuilder("bash", "-c", hashCmd)
.redirectError(ProcessBuilder.Redirect.INHERIT)
.start()
.inputStream
.bufferedReader()
.readLine()
.split("\\s".toRegex())[0]
val expectedHash = "expected‑sha256‑value‑here"
assertEquals(expectedHash, hash, "File hash mismatch")
}
These snippets illustrate how you can combine Espresso for UI navigation, UIAutomator for system dialogs, and raw ADB commands for permission and file checks. By parameterizing the test data (file size, MIME type, destination), you can reuse the same test class to cover many rows of the matrix defined earlier.
Tooling and Libraries for Data Export Verification
Beyond the built‑in Android testing framework, several open‑source and commercial tools simplify specific aspects of export testing.
| Tool / Library | Primary Use | How it Helps Export Testing |
|---|---|---|
| Android Studio Layout Inspector | Inspect view hierarchy at runtime | Verify that export button has proper contentDescription and contrast ratios without launching external scanners. |
| Accessibility Scanner (Google) | Automated accessibility audit | Detect missing labels, touch target size issues, and announce‑missing patterns on export screens. |
| Firebase Test Lab | Run instrumentation tests on a matrix of real devices | Export behavior can vary across OEM skins; Test Lab lets you execute your Espresso/UIAutomator suite on dozens of device models in parallel. |
ADB Shell + run-as | Direct file access on non‑rooted devices | Allows you to pull exported files from the app’s private storage for checksum or content analysis without rooting. |
| Hypertext‑Transfer‑Protocol (HTTP) mock server (e.g., MockWebServer) | Simulate backend responses that trigger export | When export depends on server‑downloaded data, you can control payload size, error codes, and latency to test edge cases like interrupted downloads. |
| Android Storage Access Framework (SAF) Test App | Provides a dummy “Documents” provider for deterministic testing | Replace the real file picker with a known‑good provider that returns a pre‑created file, removing flakiness caused by user‑chosen folders. |
| SUSATest autonomous agent | Exploratory, persona‑driven testing | The agent can be pointed at the app’s APK; it will autonomously tap export buttons, vary personas (impatient, elderly, power user), and surface issues such as missed permission dialogues or chooser crashes that scripted tests never reach. |
| LeakCanary | Memory leak detection | While not specific to export, it helps catch cases where export holds onto large bitmaps or cursors after completion, leading to OOM on repeated exports. |
| Stetho (Facebook) or Flipper | Runtime inspection of databases, shared preferences, network | Useful to confirm that the data source intended for export hasn’t been mutated mid‑export (e.g., a background sync clearing a table). |
Example: Using Accessibility Scanner in a CI step
Add this to your CI script (assuming you have an emulator or connected device):
# Install Accessibility Scanner APK (once)
adb install -r path/to/accessibilityscanner.apk
# Launch the scanner on your app’s package
adb shell am start -n com.google.android.accessibility.framework/.AccessibilityScannerActivity \
-e com.google.android.accessibility.framework.EXTRA_TARGET_PACKAGE com.example.app
# Wait a few seconds, then capture the report
adb shell screencap -p /sdcard/scan.png
adb pull /sdcard/scan.png ./reports/accessibility_scan.png
The resulting image highlights any export button lacking a label or with insufficient contrast, letting you fix the issue before the next release.
Example: Using MockWebServer to simulate a flaky export backend
val mockWebServer = MockWebServer()
mockWebServer.start()
val baseUrl = mockWebServer.url("/").toString()
// Configure your app to use baseUrl via DI or manifest meta-data
// Simulate a 20‑second delay then a 500 error
mockWebServer.enqueue(MockResponse().setBodyDelay(TimeUnit.SECONDS.toMillis(20), TimeUnit.MILLISECONDS))
mockWebServer.enqueue(MockResponse().setResponseCode(500))
// Run export test – expect the app to show a timeout/retry dialog
onView(withText("Export failed – try again?")).check(matches(isDisplayed()))
mockWebServer.shutdown()
These tools, combined with the Espresso/UIAutomator core, give you a robust automated harness that can be run on every pull request.
Autonomous, Persona‑Driven Exploration (Where SUSA Fits)
Scripted tests excel at verifying known paths, but they often miss the combinations that real users trigger—especially when export is entangled with other features like background sync, dark mode, or gesture navigation. Autonomous testing platforms address this gap by generating varied interaction sequences without hard‑coded steps.
How it works
- Ingestion – You upload the APK (or point the agent at a web‑view URL). The agent installs the app on a cloud‑based Android emulator or a real device farm.
- Persona modeling – Each persona (curious, impatient, novice, adversarial, elderly, accessibility‑focused, power user, etc.) defines a probability distribution over actions: tap latency, scroll speed, likelihood to long‑press, tendency to ignore dialogs, and preferred input methods (voice, switch control).
- Exploration engine – The agent builds a state‑transition graph of screens, treating each UI element as a node and each interaction as an edge. It uses reinforcement learning to prioritize edges that have not been visited or that previously led to crashes/exceptions.
- Export‑specific heuristics – The agent recognizes common export triggers (share button,
ACTION_SENDintent,FileProviderURIs) and automatically attempts to follow them with varying parameters: different MIME types, alternate chooser targets, file size extremes, and interrupted flows (by simulating low battery or storage loss mid‑action). - Observability – While exploring, the agent logs:
- Crash stack traces (
tombstonefiles) - ANR traces
- Accessibility events (missing announcements)
- File system changes (new files, permission changes)
- Network calls (to detect unintended data leakage)
- Learning loop – After each run, the agent updates its model: dead ends (e.g., a chooser that consistently returns
RESULT_CANCELED) are deprioritized, while successful export paths are reinforced for future runs. Over successive sessions, the agent becomes smarter at reaching edge‑case export scenarios that a static test suite would never consider.
Why this matters for data export
- Permission races – An impatient persona may tap export before the app finishes requesting runtime permission, exposing a race condition that leads to a silent failure.
- Chooser fatigue – A power‑user persona might repeatedly open the chooser, quickly cancel, and try again, revealing bugs where the app leaves behind temporary files or fails to reset its internal state.
- Accessibility paths – An elderly or accessibility‑focused persona uses larger fonts and talkback; the agent can verify that export success messages are announced correctly even when UI scaling changes layout.
- Adversarial input – An adversarial persona may attempt to export with a file name containing path‑traversal sequences (
../../etc/passwd) or unsupported Unicode, checking whether the app sanitizes inputs before handing them toFileProvider.
Integrating SUSA into your workflow
Add a step to your CI pipeline after unit tests but before full‑scale instrumentation tests:
# .github/workflows/susa.yml
name: SUSA Export Exploration
on: [push, pull_request]
jobs:
explore:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build APK
run: ./gradlew assembleDebug
- name: Upload to SUSA
uses: susatest/action@v1
with:
apk_path: app/build/outputs/apk/debug/app-debug.apk
personas: curious,impatient,elderly,accessibility,adversarial
max_minutes: 10
export_focus: true # tells the agent to prioritize export‑related UI
The action returns a JSON report highlighting any crashes, ANRs, accessibility violations, and leaked files discovered during the autonomous run. You can then triage those findings and convert the most relevant ones into deterministic Espresso/UIAutomator tests.
Limitations to keep in mind
- The agent does not replace assertion‑based checks; it surfaces anomalies that you still need to verify with oracles (e.g., “did the exported file contain the expected data?”).
- Exploration time is bounded; for very large apps you may need to increase the
max_minutesor focus the agent on a specific activity using launch intents. - Some device‑specific OEM quirks (e.g., Xiaomi’s MIUI chooser) may not be fully represented in the emulator fleet; supplement with real‑device runs if you observe platform‑specific failures.
By combining scripted validation with autonomous, persona‑driven exploration, you gain confidence that export works not only for the happy path but also for the myriad ways real users interact with your app.
Checklist and Best Practices
Use this concise list before marking a release as export‑ready.
| ✅ Item | Why it matters | How to verify |
|---|---|---|
| All export entry points have contentDescription | TalkBack users need to know what the button does. | Run Accessibility Scanner or manually inspect with TalkBack. |
| Export intent specifies exact MIME type | Prevents “No apps can perform this action” on chooser. | Check the code where Intent.setType() is called; unit test with assertEquals("application/pdf", intent.type). |
| FileProvider paths cover every directory used for export | Avoids FileUriExposedException. | Search for FileProvider.getUriForFile and confirm each path appears in provider_paths.xml. |
| Permission handling gracefully handles denial and rationales | Users should not see a crash when they refuse storage. | Revoke permission, trigger export, assert a rationale dialog appears. |
| Large export does not cause OOM | Prevents abrupt termination on low‑end devices. | Run export with ≥10 MB payload, monitor memory via adb shell dumpsys meminfo ; assert PSS < 150 MB. |
| Exported file is created in app‑private storage or via SAF with proper mode | Prevents world‑readable leakage of sensitive data. | After export, run adb shell ls -l /path/to/file; ensure mode is -rw------- or encrypted. |
| No temporary files left after failed or cancelled export | Avoids filling up user storage over time. | Simulate low storage or press Back during export; verify no leftover files in cache or files directories. |
| Export success/failure is announced via Toast *and* accessibility announcement | Users relying on visual or auditory cues need feedback. | Enable TalkBack, trigger export, listen for start and end messages. |
| Exported content matches source data (hash or schema validation) | Guarantees correctness of the export pipeline. | Compute SHA‑256 of source and exported file; assert equality. |
| Chooser works across at least three major OEM skins (Pixel, Samsung, Xiaomi) | Some skins replace the default chooser with custom filters. | Run the same export test on a device farm or Firebase Test Lab covering those models. |
| Export respects dark mode and font scaling | UI should remain usable under user‑chosen themes. | Change system font size to largest, switch to dark theme, repeat export steps. |
| Security scan for unintended PII in exported file | Avoids GDPR/CCPA violations. | Grep exported file for patterns like email addresses, tokens, or SSNs; assert none found. |
| Automated regression suite includes at least one happy‑path, one permission‑denial, one low‑storage, and one accessibility test | Guarantees coverage of the most common failure vectors. | Check your test suite for the corresponding test methods. |
If any item fails, treat it as a blocker and fix before promoting the build to internal QA or beta.
Closing Takeaways
Data export is a deceptively simple feature that touches permissions, storage APIs, intents, file system semantics, accessibility, and security. A robust testing strategy therefore needs:
- A explicit matrix that enumerates happy path, error conditions, edge cases, accessibility, and privacy/security checks.
- Manual exploratory steps to validate assumptions about UI labels, feedback, and cleanup that scripts often overlook.
- Automated Espresso/UIAutomator tests backed by helpers for permission manipulation, chooser interaction, and file verification, enabling fast regression in CI.
- Tooling support—Layout Inspector, Accessibility Scanner, Firebase Test Lab, MockWebServer, and services like SUSATest—to cover device fragmentation, accessibility, backend flakiness, and unknown user behavior.
- Autonomous, persona‑driven exploration that surfaces bugs hidden in permission races, chooser fatigue, and atypical inputs—problems that scripted tests rarely anticipate.
When you combine these layers, you shift export testing from a “does the share button work?” checkbox to a comprehensive assurance that users can reliably, safely, and privately retrieve their data across the full spectrum of Android devices, versions, and interaction styles. Treat export as a first‑class citizen in your test suite, and you’ll see fewer support tickets, higher user confidence, and smoother compliance audits.
---
*Keep this guide bookmarked. Return to it whenever you add a new export pathway,
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