How to Test Gdpr Data Export on Android (Complete Guide)

The General Data Protection Regulation (GDPR) gives EU residents a right to data portability. When a user asks for their personal data, the controller must provide it in a structured, commonly used, m

February 08, 2026 · 15 min read · How-To Guides

Why GDPR Data Export Testing Matters on Android

The General Data Protection Regulation (GDPR) gives EU residents a right to data portability. When a user asks for their personal data, the controller must provide it in a structured, commonly used, machine‑readable format without undue delay. For Android apps that collect personal information—whether through sign‑up, in‑app purchases, location tracking, or analytics—failing to honor this request can lead to fines, reputational damage, and loss of user trust.

In practice, many teams treat the export feature as a “nice‑to‑have” checklist item and only verify that a button exists. Production bugs surface when:

Testing the export flow end‑to‑end catches these issues before they reach users. The following sections give a complete, practical guide to testing GDPR data export on Android, from legal basics to manual checks, automated scripts, and exploratory techniques that uncover hidden failures.

---

Legal and Technical Foundations of the Export Flow

What the Regulation Requires

Typical Android Implementation

  1. Trigger – A UI element (button, menu item) launches an export request, often via a WorkManager job or a foreground service to avoid ANRs.
  2. Data gathering – The app queries local databases, shared preferences, file stores, and possibly remote APIs to collect personal data.
  3. Transformation – Raw data is mapped to a DTO (Data Transfer Object) and serialized to the chosen format. Libraries such as Gson, Moshi, or Jackson are common.
  4. Output – The serialized bytes are written to a file using FileProvider to share via ACTION_SEND or saved`CTION_SEND or stored in the app’s external cache directory for direct download.
  5. Notification – The user receives a toast or notification confirming completion and providing a share intent or download link.

Understanding these steps helps you pinpoint where tests should focus: UI trigger, background worker, data aggregation, serialization, file handling, and user‑facing delivery.

---

Comprehensive Test Matrix

Below is a detailed matrix that covers happy paths, error conditions, accessibility, security, and performance aspects. Each row includes a unique identifier, a short description, preconditions, the action steps, the expected result, and a priority (P1 = critical, P2 = important, P3 = good‑to‑have).

IDCategoryDescriptionPreconditionsStepsExpected ResultPriority
EX‑01Happy PathUser initiates export via main screen button and receives a shareable file.User logged in, at least one personal data entry present.1. Tap “Export Data” button.
2. Wait for notification.
3. Tap share notification.
4. Choose “Save to Drive”.
A non‑empty file appears in the chosen location, contains all expected fields in JSON format, and is valid per schema.P1
EX‑02Happy PathExport completes within the regulatory time limit (≤30 s for small data sets).Same as EX‑01.Same as EX‑01, measure elapsed time from button tap to notification.Elapsed time ≤30 s.P1
EX‑03Error PathExport fails gracefully when external storage is unavailable.Device storage set to read‑only via adb shell sm set-enforce‑false or SD card removed.Tap export button.App shows a user‑friendly error toast, no crash, and no partial file left behind.P1
EX‑04Error PathExport handles a sudden loss of network while fetching remote user profile data.User has remote profile data; enable airplane mode after data gathering starts.Start export, toggle airplane mode after 5 s.Export finishes with locally available data, missing remote fields are omitted or flagged, no crash.P2
EX‑05Edge CaseExport works when the app is installed in a work profile (Android Enterprise).Device provisioned with work profile, app installed in work space.Switch to work profile, launch app, trigger export.File is created within work‑profile storage and can be shared via managed share sheet.P2
EX‑06Edge CaseExport respects encrypted external storage (Adoptable storage).Device has adopted SD card encrypted; set export path to external storage.Trigger export, verify file location.File resides on encrypted storage, accessible only after device unlock.P2
EX‑07AccessibilityExport button is reachable via TalkBack and has appropriate content description.TalkBack enabled.Navigate to export button using swipe gestures.TalkBack announces button purpose (“Export your data, button”).P2
EX‑08AccessibilityExported file can be opened with a screen reader (e.g., JSON read aloud).TalkBack enabled, file saved to device.Open exported file with a plain‑text viewer that supports TalkBack.Content is readable, no garbled characters.P2
EX‑09Security/PrivacyExport does not leak other users’ data (data isolation).Two user accounts on device, each with distinct data.Log in as user A, trigger export, verify file contains only A’s data. Repeat for user B.Each export contains solely the respective user’s data; no cross‑contamination.P1
EX‑10Security/PrivacyExport file is not world‑readable; uses FileProvider with correct permissions.FileProvider configured with android:exported="false" and proper .After export, use adb shell ls -l /sdcard/Android/data//files/export/ to check permissions.File has mode 600 (owner read/write only) or is only accessible via content URI.P1
EX‑11PerformanceExport handles large data sets (>10 MB) without ANR or OOM.Populate app with ~12 MB of personal data (e.g., many photos metadata).Trigger export, monitor logcat for ActivityManager ANR warnings and dalvikvm OOM.No ANR, no OOM, export completes, file size matches expected.P1
EX‑12PerformanceConcurrent export requests are serialized or rejected appropriately.Device with sufficient storage.Rapidly tap export button 5 times in 2 s.Only one export job runs; subsequent taps show “Export already in progress” toast.P2
EX‑13LocalizationExport UI and file content respect device locale (date formats, number separators).Device locale set to fr-FR.Trigger export, inspect file timestamps and numeric fields.Dates use dd/MM/yyyy, numbers use comma as decimal separator.P2
EX‑14RegressionAfter a code change that modifies data model, export still includes all fields.Add a new field “preferredLanguage” to user profile.Trigger export, verify new field appears in output with correct value.New field present, no loss of existing fields.P1
EX‑15Cross‑SessionExported file can be re‑imported (if app supports import) and data persists correctly.App provides import feature.Export data, delete local data, import the exported file, verify data matches original.Imported data equals exported data; no corruption.P2

*The matrix above can be copied into a test management tool; adjust IDs and priorities to match your process.*

---

Manual Testing Approach

Setting Up the Environment

  1. Device preparation – Use a physical Android device (API 21+) or an emulator with Google Play services. Enable Developer options, USB debugging, and allow installation via adb.
  2. Clear stateadb shell pm clear to remove cached data, then log in with a test account.
  3. Install required tools

Step‑by‑Step Manual Verification

StepActionCommand / UI InteractionWhat to Check
1Launch app and navigate to export entry pointTap the “Export Data” button on the settings screen.Button is enabled, shows correct label.
2Start exportSame tap; optionally watch logcat: `adb logcatgrep ExportWorker`.No immediate crash; a background job starts.
3Wait for completionObserve notification or toast. Use adb shell dumpsys notification to confirm.Notification appears within expected time (≤30 s for small data).
4Retrieve the fileIf using ACTION_SEND: choose “Save to Files” and note the path. If using direct download: adb pull /sdcard/Download/export.json .File exists, size >0 bytes.
5Validate formatjq . export.json > /dev/null && echo "Valid JSON"No parsing errors.
6Schema complianceCreate a schema file schema.json (using JSON Schema Draft‑07) and run ajv validate -s schema.json -d export.json.All required fields present, types match.
7Content completenessCompare exported fields against a known baseline (e.g., export from a debug build that logs all data). Use diff -u baseline.json export.json.No missing fields; values match.
8Security checkadb shell ls -l /sdcard/Download/export.json → ensure mode 600 or that file is only accessible via content URI.No world‑readable permissions.
9Error scenario – no storageRemount storage as read‑only: adb shell sm set-enforce‑false (requires root) or remove SD card, then repeat steps 1‑4.App shows error toast, no crash, no file left.
10Error scenario – network lossStart export, after 5 s enable airplane mode: adb shell svc wifi disable && adb shell svc data disable.Export finishes with local data, missing remote fields flagged.
11AccessibilityEnable TalkBack: adb shell settings put secure accessibility_enabled 1. Navigate to export button via swipe; listen to spoken label.Label describes purpose clearly.
12Clean‑upDelete exported file: adb rm /sdcard/Download/export.json. Verify no residual files in app‑private directories.Storage left clean.

Tips for Manual Testing

---

Automated Testing Strategies

Unit‑Level Validation

Export logic often resides in a ViewModel or UseCase that returns a Flow or a LiveData. Write pure‑unit tests using JUnit and Mockito:


@Test
fun `exportUseCase returns correct JSON when data present`() {
    // Given
    val fakeRepo = mock(UserRepository::class.java)
    whenever(fakeRepo.getPersonalData())
        .thenReturn(listOf(
            UserData(name = "Ada", email = "ada@example.com", birthDate = LocalDate.of(1815,12,10))
        ))
    val useCase = ExportUseCase(fakeRepo)

    // When
    val result = runBlocking { useCase.execute() }

    // Then
    assertThat(result.json).isNotBlank()
    val obj = JSONObject(result.json)
    assertThat(obj.getString("name")).isEqualTo("Ada")
    assertThat(obj.getString("email")).isEqualTo("ada@example.com")
}

*Validate that the serializer (Gson/Moshi) does not drop null fields unless intended.*

Instrumented UI Tests (Espresso)

Espresso can click the export button and wait for a notification:


@Test
fun exportButton_showsNotification() {
    // Given a logged‑in state
    onView(withId(R.id.btn_export)).perform(click())

    // Wait for the notification to appear (custom IdlingResource)
    val notifIdling = NotificationIdlingResource("Export completed")
    IdlingRegistry.getInstance().register(notifIdling)

    // Then verify notification text
    onView(withText(R.string.export_success))
        .check(matches(isDisplayed()))

    IdlingRegistry.getInstance().remove(notifIdling)
}

*Create a simple IdlingResource that polls NotificationManager.getActiveNotifications() for a notification with the export tag.*

File‑Output Verification (UIAutomator + ADB)

When the export uses the Storage Access Framework, you can capture the returned URI:


@Test
fun exportUri_isValidAndNotEmpty() {
    // Launch the export flow that ends with ACTION_CREATE_DOCUMENT
    val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        setType("application/json")
    }
    activityRule.activity.startActivityForResult(intent, REQUEST_EXPORT)

    // Wait for result
    val resultData = activityRule.activity.waitForActivityResult(REQUEST_EXPORT)
    val uri = resultData?.data
    assertNotNull(uri)

    // Read content via ContentResolver
    val input = activityRule.activity.contentResolver.openInputStream(uri)
    val content = input?.bufferedReader()?.readText()
    assertNotNull(content)
    assertTrue(content.isNotBlank())
}

*This test works on both emulators and physical devices without needing root.*

Using WorkManager Test API

If the export is delegated to a Worker, you can test it synchronously:


@Test
fun exportWorker_createsFile() {
    val context = ApplicationProvider.getApplicationContext()
    val worker = ExportWorker(
        context,
        WorkerParameters.fromTrivialTags()
    )
    val result = worker.doWork()
    assertThat(result).isEqualTo(Result.success())

    val outputFile = File(context.getExternalFilesDir(null), "export.json")
    assertThat(outputFile).exists()
    val json = File(outputFile).readText()
    assertThat(json).contains("\"name\"")
}

*Leverage TestListenableWorker or ExecutorTestRule for deterministic execution.*

Automated Security Checks

Performance & Stress Testing

Integrating Autonomous Exploration (SUSATest)

SUSATest can be pointed at the APK or a build variant and left to explore the app using its built‑in personas. Because it exercises random interaction sequences, it will:

A typical command line invocation:


pip install susatest-agent
susatest run \
    --apk path/to/app-debug.apk \
    --personas curious impatient elderly \
    --export-dir ./susa-output \
    --max-steps 2000 \
    --timeout 1800

*The agent will generate a report that lists any export‑related failures, complete with device logs and screenshots.*

---

Edge Cases That Surface Only in Production

Even with exhaustive manual and automated suites, certain conditions are hard to reproduce in a lab. Below are the most common production‑only pitfalls for GDPR export on Android, along with detection strategies.

SituationWhy It’s Missed in Test LabsDetection / Mitigation
Adoptable storage encryption changes mid‑exportEmulators rarely emulate adoptable storage; physical test devices may not have an SD card inserted.Use adb shell sm set-encrypt-adoptable true/false on a rooted device to toggle encryption while the export worker runs; observe for FileNotFoundException or partial files.
User switches to a second profile (work/personal) while export is pendingMost test scripts stay in a single user session.Use adb shell am switch-user after starting export; verify that the export completes in the originating profile and does not leak into the other profile’s storage.
Background kill due to system low‑memory while Worker is runningTest devices often have ample RAM; the system may not kill the worker.Simulate pressure with adb shell am kill or adb shell cmd activity clear-top while the export is in progress; ensure the worker is rescheduled and retries.
Network handoff (Wi‑Fi → cellular) causing intermittent lossLab networks are static.Use adb shell svc wifi enable && adb shell svc wifi disable in a loop during export; check that the worker gracefully retries remote fetches and does not crash.
Multiple simultaneous export requests from different apps using same FileProvider authorityTest usually isolates the app under test.Install a second test app that shares the same FileProvider authority; trigger exports in both and confirm that each gets its own content URI without interference.
Locale change while export is in flightTests typically set locale before launch and never change it.Use adb shell am broadcast -a android.intent.action.LOCALE_CHANGED --es com.android.internal.intent.extra.LOCALE fr during export; verify that timestamps in the output reflect the new locale.
External storage mounted as USB MTP (mass storage) causing file lockRarely reproduced unless device is connected to a PC in MTP mode.Connect device to a host PC, enable MTP (adb usb), start export, and verify that the write either fails with a clear error or waits until the connection is released.
Battery‑saver or Doze mode deferring WorkManagerTest runs often keep device plugged in and awake.Run adb shell dumpsys battery set level 5 and adb shell dumpsys battery set status 2 (charging false) then enable Doze with adb shell dumpsys deviceidle force-idle; start export and confirm that the job is delayed but eventually runs.
Corrupt shared‑preference file due to abnormal process terminationHard to provoke without forcing a crash.Kill the app’s process mid‑write to SharedPreferences using adb shell kill $(pidof ); after restart, trigger export and ensure the worker can still read the valid entries and skips the corrupted ones.
Google Play Store update occurring during exportUpdate flow replaces the APK while the old process may still be alive.Use adb shell pm install -r path/to/new.apk while the export worker is active; confirm that either the export completes with the old version or is safely aborted without leaving a half‑written file.

Mitigation patterns:

---

Quick Reference Checklist

If any item fails, mark the test as blocked, investigate the root cause, and add a unit or instrumented test to prevent regression.

---

Closing Takeaways

The GDPR data export feature is more than a UI button; it is a end‑to‑end pipeline that touches data gathering, background work, serialization, file handling, and user‑facing delivery. A robust test strategy must therefore span:

  1. Specification‑driven checks – validate that the output contains every piece of personal data the app processes, in a machine‑readable format, and that it arrives within the legal time window.
  2. Failure‑mode testing – simulate storage loss, network interruptions, process kills, and low‑resource states to confirm the app degrades gracefully and never leaves corrupt or leaked data behind.
  3. Accessibility and localisation – ensure that users who rely on assistive technologies or non‑default locales can invoke the export and understand the result.
  4. Security verification – enforce file‑privacy boundaries, confirm no cross‑user contamination, and guarantee that temporary artefacts are cleaned.
  5. Production‑realism – adopt stressors that only appear in the wild (adoptable storage encryption flips, profile switches, USB MTP locks, battery‑saver deferrals) to uncover hidden bugs.

By combining a clear test matrix, disciplined manual verification, layered automated checks (unit, Espresso/UIAutomator, WorkManager), and exploratory, persona‑driven tools like SUSATest, you gain confidence that the export function satisfies both legal obligations and user expectations. Treat the export as a critical path, not an after‑thought, and your Android app will stay compliant, trustworthy, and resilient in the hands of real users.

---

*Feel free to copy the tables, code snippets, and checklist into your test repository or test‑management tool. Adjust identifiers, timeouts, and file paths to match your project’s conventions, and keep the checklist handy before each release.*

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