How to Write Test Cases for Permission Dialogs (With Examples)

How to Write Test Cases for Permission Dialogs (With Examples)

April 04, 2026 · 17 min read · How-To Guides

How to Write Test Cases for Permission Dialogs (With Examples)

Permission dialogs are system‑mediated prompts that ask users to grant or deny access to protected resources such as location, camera, contacts, or storage. Because they appear asynchronously, can be influenced by device policy, and directly affect app functionality and privacy compliance, they deserve a dedicated test‑case suite. This guide walks you through the anatomy of a high‑signal test case, shows how to cover positive, negative, edge, and boundary scenarios, provides a concrete matrix of 20+ examples, explains data setup and prioritization, and details both manual and automated execution approaches. Throughout, we reference how an autonomous QA platform like SUSA can augment the process without turning the article into a sales pitch.

How to Write Test Cases for Permission Dialogs (With Examples): Test Case Anatomy

A test case that reliably validates a permission dialog consists of five essential fields: identifier, preconditions, steps, expected result, and post‑conditions (clean‑up). Each field must be explicit enough that another engineer can reproduce the test on any device or emulator without guessing.

Identifier (ID) – Use a short, hierarchical code that reflects the feature area and the permission under test. Example: PERM_LOC_01 for the first location‑permission test. Prefixes make it easy to filter in test‑management tools and to trace back to requirements.

Preconditions – List the exact device state required before the first step. This includes:

If a precondition can be set via a command, include that command (see the Data Setup section later). Ambiguous preconditions lead to flaky results and wasted effort.

Steps – Write each action as an imperative sentence, using the UI element’s role rather than its positional coordinates. Good steps avoid hard‑coded pixel values and instead rely on accessibility IDs, content‑descriptions, or text that is stable across locales. For a permission dialog, typical steps are:

  1. Launch the activity that triggers the permission request.
  2. Wait for the system dialog to appear (use an explicit wait, not a sleep).
  3. Tap the button that corresponds to the desired user choice (Allow, Deny, or “Don’t ask again”).
  4. Verify the subsequent app behavior or system state.

If a step involves a system setting (e.g., turning off battery optimization), note the exact path: Settings → Apps → [YourApp] → Battery → Unrestricted.

Expected Result – State the observable outcome that determines pass or fail. For permission tests, this is usually:

Post‑conditions – Return the device to a known state so the next test starts clean. This often means revoking the permission via adb shell pm revoke or clearing app data. Include the clean‑up command in the test case document to avoid state bleed.

When these five fields are filled with concrete, unambiguous information, the test case becomes a reliable artifact that can be executed manually, scripted, or fed into an autonomous explorer.

How to Write Test Cases for Permission Dialogs (With Examples): Positive and Negative Scenarios

Permission testing naturally splits into three outcome categories: granting the permission, denying it, and encountering a system‑level restriction. Each category yields distinct test ideas.

Granting Permission on First Prompt

The most common path is the user tapping “Allow” when the dialog appears for the first time. Test cases in this group verify that:

Denying Permission (Ask Again Later)

When the user selects “Deny” but leaves the option to be asked again, the app should:

Denying with “Never Ask Again”

Choosing “Deny” and checking the “Don’t ask again” box triggers a permanent denial until the user manually changes the setting in system Settings. Test cases must verify:

System‑Level Denial (Device Policy or Enterprise Restriction)

On managed devices, an administrator can disable certain permissions via a Device Policy Controller (DPC). In this scenario:

Covering these four categories ensures that the app behaves correctly regardless of how the user or the device decides to grant access.

How to Write Test Cases for Permission Dialogs (With Examples): Edge and Boundary Cases

Beyond the straightforward allow/deny paths, permission dialogs expose edge conditions that only surface under specific device states, rapid interactions, or localization quirks. Treat these as boundary tests because they push the system to its limits.

Rapid Successive Prompts

If an app requests multiple permissions in quick succession (e.g., location then camera), the system may queue dialogs or collapse them. Test steps:

  1. Trigger a request for Permission A.
  2. Before the dialog appears, trigger a request for Permission B.
  3. Observe whether the system shows one dialog, two dialogs, or merges the requests.
  4. Verify that the callbacks for each permission are delivered correctly and that the UI does not become unresponsive.

Permission Dialog Appears Under an Overlay

Some apps display custom overlays (e.g., chat heads, screen‑filter layers). If an overlay sits above the system dialog, the user may be unable to tap the buttons. Test steps:

  1. Enable an overlay app that draws over other apps (e.g., a screen‑dim filter).
  2. Trigger the permission request.
  3. Attempt to interact with the dialog via accessibility services or UIAutomator.
  4. Confirm that the dialog remains tappable and that the overlay does not block input. Log any failure as a potential accessibility issue.

Locale Changes Affecting Dialog Text

The text of permission dialogs is localized. If your app relies on string matching (e.g., using Espresso’s withText("Allow")) the test will fail in non‑English locales. Test steps:

  1. Change device language to a right‑to‑left language (e.g., Arabic) or a language with longer strings (e.g., German).
  2. Trigger the permission request.
  3. Verify that the dialog appears with the correct localized text.
  4. If your automation uses text‑based locators, ensure they are replaced with resource‑ID or content‑description locators that are locale‑independent.

Dialog Appears After Activity Recreation (Configuration Change)

Rotating the device or changing font scale can cause the activity to recreate while a permission request is pending. Test steps:

  1. Trigger a permission request.
  2. Before the user responds, rotate the device to landscape.
  3. Verify that the dialog remains on screen and is not dismissed.
  4. After the user responds, ensure the activity’s state (e.g., ViewModel data) is correctly restored and the permission callback is received.

Permission Request During Background Execution

Starting with Android 12, apps targeting API 31+ cannot launch permission dialogs from the background unless they meet specific exceptions. Test steps:

  1. Move the app to the background (e.g., press Home).
  2. From a background service or WorkManager, attempt to request a sensitive permission.
  3. Confirm that the system either denies the request silently or shows a toast indicating the restriction.
  4. Ensure the app does not crash and logs the background restriction appropriately.

These edge cases often escape manual testing because they depend on timing or device state. Including them in your test matrix raises the likelihood of catching production‑only bugs.

How to Write Test Cases for Permission Dialogs (With Examples): Data Setup and Test Environment

Reliable permission tests start with a known permission state. The Android Debug Bridge (adb) provides deterministic ways to grant, revoke, or reset permissions. Complementing adb with an autonomous explorer like SUSA can reduce manual setup and uncover hidden states.

Using adb to Reset Permissions

Before each test, set the permission to a known baseline:


# Revoke a permission (sets to denied, not “never ask again”)
adb shell pm revoke com.example.app android.permission.ACCESS_FINE_LOCATION

# Grant a permission
adb shell pm grant com.example.app android.permission.CAMERA

# Simulate “never ask again” by adding the app to the ignored list
adb shell appops set com.example.app ACCESS_FINE_LOCATION ignore

Include these commands in the preconditions field of your test case. If you need to restore the default state after a test, run the revoke command again or clear app data:


adb shell pm clear com.example.app

Clearing data also removes any persisted “never ask again” flags, returning the permission to the “not determined” state.

Using SUSA CLI for Exploration

SUSA can be installed via pip install susatest-agent and pointed at an APK or a device. To generate a baseline exploration that surfaces permission‑related screens:


susatest explore --app-path ./app-debug.apk --device emulator-5554 --output ./susa-report

The explorer will autonomously trigger permission dialogs as part of its normal flow discovery. After the run, you can inspect the report to see which dialogs were encountered, how the autonomous personas reacted, and whether any unexpected states (e.g., dialog under overlay) appeared. Use this output to enrich your manual test cases with realistic preconditions that you might not have considered.

Mocking System Dialogs in Unit Tests

For fast feedback, unit tests can mock the permission‑checking layer using frameworks like Mockito or Robolectric. Example with Robolectric:


@Test
public void locationPermissionGranted_startsMap() {
    RuntimePermissionsDispatcher.setPermissionResult(
            Manifest.permission.ACCESS_FINE_LOCATION,
            PackageManager.PERMISSION_GRANTED);
    ActivityScenario.launch(MainActivity.class);
    onView(withId(R.id.mapView)).check(matches(isDisplayed()));
}

While unit tests cannot verify the actual system dialog UI, they confirm that your app’s logic branches correctly based on the permission result. Pair these with instrumented tests that validate the UI to achieve full coverage.

How to Write Test Cases for Permission Dialogs (With Examples): Prioritization and Traceability

Not all permission tests carry equal weight. Use a risk‑based matrix to decide which cases to automate first, which to keep as manual exploratory checks, and which to document for compliance audits.

Risk‑Based Priority Matrix

Impact \ LikelihoodHigh (frequent)Medium (occasional)Low (rare)
High (security, privacy violation, core feature blocked)P0 – Automate & run on every commitP1 – Automate, run nightlyP2 – Manual, regression‑only
Medium (UX degradation, optional feature)P1 – Automate, run nightlyP2 – Manual, sprint‑levelP3 – Optional, documentation
Low (cosmetic, edge‑case unlikely in prod)P2 – Manual, sprint‑levelP3 – Documentation onlyP4 – Skip

Assign each test case an ID and a priority based on where it lands in the matrix. For example, a test that verifies the app redirects to Settings after a permanent denial (high impact, high likelihood) becomes a P0 candidate for automation.

Linking to Requirements

Trace each test case to a specific requirement artifact:

Record the requirement ID in the test case’s “References” field. Most test‑management tools (e.g., Zephyr, TestRail) allow you to link test cases to Jira epics or user stories, providing an audit trail for regulators or internal reviews.

Maintaining Traceability in Test Management

When you add a new permission test:

  1. Create the test case with its ID, preconditions, steps, expected result, and post‑conditions.
  2. Add a “Related Requirements” list (e.g., REQ‑PERM‑001, REQ‑PRIV‑004).
  3. Set the priority field according to the matrix.
  4. Tag the test with labels like permission, android, automation for easy filtering.
  5. After each run, update the “Last Executed” and “Result” fields. If a test fails, link the defect to the test case for root‑cause analysis.

This traceability ensures that when a permission‑related requirement changes (e.g., a new scoped storage rule), you can quickly locate all affected tests and update them.

How to Write Test Cases for Permission Dialogs (With Examples): Worked Test Matrix (20+ Examples)

Below is a consolidated table that captures a broad spectrum of permission‑dialog scenarios. Each row includes an ID, preconditions, steps, and expected result. Feel free to copy‑paste into your test‑management tool and adapt the package name (com.example.app) and permission strings to your own app.

IDPreconditionsStepsExpected Result
PERM_LOC_01Device API 33, app installed, location permission not determined1. Launch MainActivity.
2. Tap “Enable Location” button.
3. Wait for system location dialog.
4. Tap Allow.
Location permission granted (pm grant …).
Map fragment appears and shows user’s current location.
No crash or ANR.
PERM_LOC_02Location permission denied (not “never ask again”)Same steps as PERM_LOC_01, but tap Deny in step 4.Permission remains denied.
Map fragment shows a placeholder with text “Location access denied”.
App logs DENIED callback.
PERM_LOC_03Location permission never ask again (set via appops ignore)Same steps as PERM_LOC_01.System does not show location dialog.
Permission check returns PERMISSION_DENIED with shouldShowRequestPermissionRationale == false.
App displays a snackbar prompting user to go to Settings.
Tapping the snackbar opens Settings.ACTION_APPLICATION_DETAILS_SETTINGS.
PERM_CAM_01Camera permission not determined, device not in call1. Open ProfileFragment.
2. Tap “Take Photo”.
3. Wait for camera dialog.
4. Tap Allow.
Camera permission granted.
Camera preview starts within 2 seconds.
Photo can be captured and saved.
PERM_CAM_02Camera permission deniedSame steps as PERM_CAM_01, tap Deny.Permission remains denied.
Photo button shows a toast “Camera permission required”.
No crash when attempting to open camera.
PERM_CAM_03Camera permission never ask againSame steps as PERM_CAM_01.No dialog shown.
Permission check returns denied with rationale flag false.
App disables the photo button and shows an explanatory banner.
PERM_CONTS_01Contacts permission not determined1. Navigate to InviteFriendsScreen.
2. Tap “Import Contacts”.
3. Wait for contacts dialog.
4. Tap Allow.
Contacts permission granted.
Contact picker loads and displays at least one contact.
Selected contact can be added to invite list.
PERM_CONTS_02Contacts permission deniedSame steps as PERM_CONTS_01, tap Deny.Permission denied.
Import button shows a snackbar “Cannot access contacts”.
App does not crash.
PERM_CONTS_03Contacts permission never ask againSame steps as PERM_CONTS_01.System silently denies.
App shows a dialog explaining why contacts are needed and a button to open Settings.
Settings screen opens correctly.
PERM_SMS_01SMS permission not determined, device API ≥33 (runtime)1. Open VerificationScreen.
2. Tap “Send Code via SMS”.
3. Wait for SMS dialog.
4. Tap Allow.
SMS permission granted.
App can send SMS and receives delivery callback.
Verification code is auto‑filled if SMS Retriever API used.
PERM_SMS_02SMS permission deniedSame steps as PERM_SMS_01, tap Deny.Permission denied.
App shows error “Unable to send SMS”.
Falls back to email verification path.
PERM_SMS_03SMS permission never ask againSame steps as PERM_SMS_01.No dialog shown.
Permission check returns denied with rationale false.
App hides the SMS option and shows only email verification.
PERM_BODYSENSORS_01Body sensors permission not determined, API ≥291. Start HealthActivity.
2. Tap “Start Heart Rate Monitor”.
3. Wait for body‑sensor dialog.
4. Tap Allow.
Permission granted.
Heart‑rate sensor begins streaming data.
Data displayed in real‑time chart.
PERM_BODYSENSORS_02Body sensors permission deniedSame steps as PERM_BODYSENSORS_01, tap Deny.Permission denied.
Heart‑rate button disabled, toast “Sensor access required”.
No crash.
PERM_BODYSENSORS_03Body sensors permission never ask againSame steps as PERM_BODYSENSORS_01.System does not show dialog.
App shows a persistent banner “Enable body‑sensor access in Settings”.
Tapping banner opens Settings.
PERM_BACKGROUNDLOC_01Target API 33, background location permission not determined1. Enable foreground location first (grant via step 1 of PERM_LOC_01).
2. Start a foreground service that requests background location.
3. Wait for background‑location dialog.
4. Tap Allow.
Background location granted.
Service receives location updates even when app is in background.
Battery usage shown in Settings > Apps > [App] > Battery.
PERM_BACKGROUNDLOC_02Background location deniedSame steps as PERM_BACKGROUNDLOC_01, tap Deny.Background location denied.
Foreground location still works (if previously granted).
Service stops receiving updates and logs denial.
PERM_BACKGROUNDLOC_03Background location never ask againSame steps as PERM_BACKGROUNDLOC_01.No dialog shown.
Background location requests are ignored silently.
App shows a notification prompting user to enable background location in Settings.
PERM_OVERLAY_01Device with overlay permission not determined, overlay app active (e.g., screen‑filter)1. Launch app that requests overlay permission (e.g., chat‑head feature).
2. Wait for overlay dialog.
3. Tap Allow.
Overlay permission granted.
Chat‑head appears and can be dragged.
Underlying app remains usable.
PERM_OVERLAY_02Overlay permission deniedSame steps as PERM_OVERLAY_01, tap Deny.Overlay permission denied.
Chat‑head feature button shows toast “Overlay permission needed”.
No crash.
PERM_OVERLAY_03Overlay permission never ask againSame steps as PERM_OVERLAY_01.System does not show dialog.
App disables chat‑head button and shows a Settings shortcut.
PERM_NOTIFICATION_01Notification permission not determined, API ≥331. Open SettingsScreen.
2. Toggle “Enable notifications”.
3. Wait for notification dialog.
4. Tap Allow.
Notification permission granted.
App can post a test notification that appears in the shade.
Notification channel importance set correctly.
PERM_NOTIFICATION_02Notification permission deniedSame steps as PERM_NOTIFICATION_01, tap Deny.Permission denied.
Toggle remains off, toast “Notifications disabled”.
Attempting to post a notification results in silent drop (no exception).
PERM_NOTIFICATION_03Notification permission never ask againSame steps as PERM_NOTIFICATION_01.No dialog shown.
App shows a banner “Enable notifications in Settings”.
Tapping banner launches the notification settings screen.

How to use the table

How to Write Test Cases for Permission Dialogs (With Examples): Manual Execution Approach

Even with strong automation, manual testing remains valuable for exploring subtle UX issues, verifying accessibility, and validating behaviors that are hard to script (e.g., user perception of dialog timing). A disciplined manual process ensures that the automated suite stays aligned with real‑world usage.

Test Lab Setup

  1. Device matrix – Maintain a small set of physical devices covering the major OS versions you support (e.g., Android 10, 11, 12, 13) and at least one emulator for API level testing.
  2. Tooling – Install adb, Android Studio (for logcat), and optionally UI Automator Viewer to inspect element attributes.
  3. Data‑reset script – Create a shell script that runs the adb revoke/grant commands listed in the Data Setup section before each test case. Store the script in version control so every tester can reproduce the exact starting state.
  4. Evidence capture – Use adb shell screenrecord or the built‑in screen‑recording feature on the device to capture video of each test run. Save the clip alongside the test case ID for later review.

Execution Steps

For each test case:

  1. Run the reset script to achieve the precondition.
  2. Launch the app and navigate to the screen that triggers the permission request.
  3. Perform the steps exactly as written, noting any deviations (e.g., dialog does not appear).
  4. Observe the expected result and record Pass/Fail.
  5. If the result is Fail, capture a screenshot, logcat snippet, and the screen recording.
  6. Execute the post‑condition clean‑up script.

Exploratory Augmentation with SUSA

After completing the scripted manual suite, launch SUSA in exploratory mode to see whether its autonomous personas discover additional permission‑related states:


susatest explore --app-path ./app-debug.apk --device emulator-5554 --personas all --timeout 15m

Review the generated report for:

Add any new findings as additional test cases, assigning them IDs and priorities based on the risk matrix. This hybrid approach leverages the repeatability of scripted manual tests and the breadth of autonomous exploration.

How to Write Test Cases for Permission Dialogs (With Examples): Automated Execution Approach

Automated tests give you fast feedback on every commit and enable regression validation across device farms. The key to stable permission tests is handling the asynchronous nature of system dialogs and avoiding brittle locators.

Instrumented UI Tests (Espresso/UIAutomator)

Espresso works well for in‑app interactions but cannot directly interact with system dialogs. Use UIAutomator to tap system buttons, then return control to Espresso for in‑app validation.

Example Kotlin test for location permission:


@Test
fun locationPermissionGranted_showsMap() {
    // Revoke to ensure a clean state
    AdbShellCommand("pm revoke ${ApplicationProvider.getApplicationContext().packageName} android.permission.ACCESS_FINE_LOCATION")
        .execute()

    // Launch activity
    ActivityScenario.launch(MainActivity::class.java)

    // Trigger permission request
    onView(withId(R.id.btnEnableLocation)).perform(click())

    // Wait for system dialog and click Allow (UIAutomator)
    val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
    val allowButton = uiDevice.findObject(By.text("Allow"))
    assertTrue(allowButton.waitForExists(5000))
    allowButton.click()

    // Return to Espresso to verify in‑app state
    onView(withId(R.id.mapView)).check(matches(isDisplayed()))
}

Key points:

Flakiness mitigation:

Web Permission Tests (Playwright)

For web apps, permission dialogs are browser‑mediated (geolocation, notifications, camera). Playwright can handle them natively.

Example TypeScript test for geolocation:


test('geolocation permission granted shows map', async ({ page }) => {
    // Clear prior permissions
    await page.context().clearPermissions();

    // Navigate to page that requests location
    await page.goto('https://example.com/map');

    // Expect the permission dialog and grant it
    await page.waitForEvent('dialog', async dialog => {
        await dialog.accept(); // equivalent to clicking Allow
    });

    // Trigger the request (e.g., click a button)
    await page.click('#enable-location');

    // Verify map appears
    await expect(page.locator('#map')).toBeVisible({ timeout: 5000 });
});

Notes:

Leveraging SUSA for Regression Script Generation

SUSA can observe the flows it explores and emit ready‑to‑run automation scripts. After an exploratory run:


susatest generate --report ./susa-report --output ./generated-tests --framework appium

The generated Appium (Android) or Playwright (Web) scripts contain:

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