Deep Links Testing Checklist (2026)

Deep Links Testing Checklist (2026) provides a concrete, actionable list of test items that engineers can use to verify deep link behavior across Android and iOS apps. The checklist groups more than t

March 22, 2026 · 14 min read · Testing Checklists

Deep Links Testing Checklist (2026) provides a concrete, actionable list of test items that engineers can use to verify deep link behavior across Android and iOS apps. The checklist groups more than thirty specific checks into logical areas—happy path, error handling, edge/boundary cases, accessibility, security/privacy, performance, and release readiness—so teams can copy‑paste it into test plans, automate the repetitive parts, and focus manual effort on the nuances that only appear in production. Each item.

Happy Path Testing

Basic URI scheme validation

Confirm that the app registers the expected scheme (e.g., myapp://) and that the operating system routes the intent to the correct activity. On Android, run:


adb shell am start -W -a android.intent.action.VIEW -d "myapp://home" com.example.myapp

Look for Status: ok and Activity: com.example.myapp/.MainActivity. On iOS, use xcrun simctl openurl booted "myapp://home" and verify the app launches to the home screen. Pass criteria: the intended screen appears without user interaction, and no fallback to a web page occurs unless explicitly configured.

Parameter parsing and default values

Deep links often carry query parameters that drive navigation or pre‑fill forms. Test a matrix of parameter combinations:

Parameter setExpected screenPre‑filled fieldsPass/Fail
?section=profileProfile tabNone
?section=settings&tab=notificationsSettings → NotificationsNone
?productId=12345&qty=2Product detailQty = 2
(empty)Home screenNone
?invalid=fooHome screen (ignore unknown)None

Automate with a data‑driven test that feeds each URL to the app via adb shell am start or xcrun simctl openurl. Verify that the UI reflects the values and that any required defaults (e.g., default tab = first) are applied when a parameter is omitted.

Handling of optional path segments

Some apps treat path segments as optional navigational hints (e.g., myapp://order/12345 vs myapp://order). Create two test groups: one with the full path, one with only the base. Both should resolve to the same screen; the longer path may additionally scroll to or highlight the referenced item. Confirm that extra segments beyond the defined pattern are ignored rather than causing a crash.

Error Handling

Malformed deep links

Test URLs that break URI syntax: missing scheme (://home), double slashes (myapp:////home), percent‑encoded errors (myapp://%GGhome), or embedded line breaks. The OS should either refuse to launch the app or deliver the intent with data null. Inside the app, guard against null or malformed Uri objects and show a graceful fallback (e.g., toast “Invalid link”). Pass criteria: no crash, no ANR, and a clear user‑visible message or silent ignore as defined by product spec.

Missing required parameters

Identify parameters that the app treats as mandatory for a feature (e.g., token for a deep link that opens a secured page). Launch the link without the token and verify that the app either redirects to a login screen, shows an error dialog, or falls back to a public landing page. Record the exact flow and ensure no stack trace is leaked to the UI.

Unsupported schemes and host authority

If the app only handles myapp://, test myapp2://home, https://myapp.com/home, and ftp://myapp.com/home. The intent should not resolve to your activity; instead, the system may open a browser or show an “Unable to open” prompt. Confirm that your manifest () does not overly broaden the host or scheme attributes, which could inadvertently capture links meant for other apps.

Deep link interception by other apps

On Android, a malicious app could register the same scheme with higher priority. Install a test app that declares for myapp://* with android:priority="1000" and verify that your app no longer receives the link. Mitigation: use Android App Links or iOS Universal Links with domain verification, and add runtime checks that the incoming intent’s package matches your own.

Edge and Boundary Cases

Extremely long URLs

Generate a deep link with a query string exceeding 2000 characters (the practical limit for many browsers and some OS intent buffers). Example:


myapp://home?`python -c "print('a'*2000)"`

Launch via adb and watch for truncation, Uri.getQueryParameter returning null, or crashes. Pass criteria: the app either safely ignores excess characters or fails gracefully with a user‑friendly message.

Unicode and special characters

Test characters outside ASCII: emojis (🚀), accented letters (café), right‑to‑left scripts (العربية), and reserved characters (?, #, &). Ensure proper percent‑encoding/decoding on both sides. On Android, use URLEncoder.encode before sending; on iOS, use addingPercentEncoding. Verify that the decoded value matches the original and that UI renders correctly (no garbled glyphs).

Deep link chaining and cyclic navigation

Some apps allow a deep link to trigger another deep link (e.g., a notification that opens myapp://settings?next=myapp://profile). Create a chain of two or three links and confirm each transition occurs without losing state. Additionally, test a deliberately cyclic link (myapp://home?next=myapp://home) to ensure the app does not enter an infinite loop; it should detect the repetition and stop after a predefined number of hops (commonly 2‑3).

Handling of trailing slashes and case sensitivity

myapp://HOME/ vs myapp://home may be treated differently depending on OS and implementation. Test both variants and document whether your app normalizes case (lowercase) and strips trailing slashes. Inconsistent handling can cause duplicate analytics entries or failed conditional logic.

Links that launch from background or killed state

Launch a deep link while the app is in the background, then repeat after swiping it away from recent apps. Verify that the app restarts, restores the correct back stack, and does not show a stale UI. On Android, check isTaskRoot and the intent extras in onNewIntent. On iOS, ensure application(_:open:options:) is called in AppDelegate and that the scene restoration works.

Accessibility Testing

Focus management after deep link launch

When a deep link opens a screen, the first focusable element should receive accessibility focus. Use TalkBack (Android) or VoiceOver (iOS) and swipe to confirm that the focus lands on a meaningful element (e.g., a heading or the first input). If the link opens a dialog, focus must be trapped inside the dialog until dismissed. Fail if focus lands on the background or is lost entirely.

Screen reader announcement of parameters

If a deep link pre‑fills a form, the screen reader should announce the pre‑filled values when the field gains focus. For a link like myapp://support?issue=login, the “Issue” field should be spoken as “Login”. Verify that the announcement is concise and does not read raw URLs.

Touch target size and spacing

Deep links often land on list items or cards. Ensure each tappable element meets the minimum 48 dp (Android) / 44 pt (iOS) guideline, with at least 8 dp/pt spacing. Use the Accessibility Scanner (Android) or Xcode Accessibility Inspector to flag violations.

Contrast and dynamic type

Verify that text displayed after a deep link meets WCAG AA contrast (4.5:1 for normal text, 3:1 for large). Test with dynamic type sizes set to largest; ensure layouts do not truncate or overlap critical information. This is especially important for deep links that navigate to settings or legal pages where users may rely on larger fonts.

Navigation hierarchy and back stack semantics

When a deep link bypasses the usual entry point, the back button should behave predictably: pressing Back should return to the logical previous screen (often the home screen) rather than exiting the app. Validate with accessibility services that the back action is announced correctly (“Go back to previous screen”).

Security and Privacy

Open redirect prevention

A deep link that accepts a redirect parameter must validate that the destination URL belongs to an allowed domain. Test with myapp://home?redirect=https://evil.com/phish. The app should reject the redirect, show an error, or ignore the parameter. Confirm that no HTTP request is made to the external domain.

Parameter injection and SQL/NoSQL safeguards

If a parameter is used directly in a database query (e.g., userId), test for injection payloads: myapp://profile?userId=1' OR '1'='1. The app should treat the value as data, not executable code. Use parameterized queries or ORM methods; verify that no unexpected data is returned or that an error is logged without exposing stack traces.

Deep link spoofing and app link verification

On Android, attempt to launch myapp://home from a web page hosted on a domain not verified in your assetlinks.json. The intent should resolve to the browser fallback, not your app. On iOS, ensure your apple-app-site-association file is correctly served and that the universal link only opens your app when the domain matches. Use the sudo /usr/libexec/LocationAssistant tool (macOS) to debug.

Sensitive data exposure in logs or crash reports

Deep links may contain authentication tokens or PII. Verify that logging frameworks (e.g., Timber, NSLog) do not output the full URL. Use a logcat filter (adb logcat | grep myapp) and confirm that token values are absent. On iOS, check the Console app for leaked URLs.

Rate limiting and abuse mitigation

If a deep link can trigger a costly operation (e.g., myapp://checkout?item=giftcard&amount=10000), test rapid successive launches (10 launches in a short window (using a shell loop). The app should enforce server‑side rate limits or client‑side debouncing to prevent abuse. Confirm that no financial transaction is processed without additional user confirmation.

Performance Testing

Cold start latency

Measure time from adb shell am start to first frame rendered for a deep link that launches the app from a stopped state. Use adb shell am start -W which reports TotalTime. Aim for <1500 ms on mid‑tier devices; note any deep‑link‑specific work (e.g., network fetch for token) that adds overhead. Capture traces with systrace or Android Studio Profiler to isolate costly steps.

Warm start latency

Launch the app, press Home, then fire the deep link again. Warm start should be noticeably faster (<800 ms) because the process remains cached. Verify that the deep link does not force a full reload of resources unnecessarily (e.g., re‑downloading assets already in memory).

Memory impact

After a deep link navigation, take a heap dump (adb shell am dumpheap com.example.myapp /data/local/tmp/hprof) and compare to the baseline after a standard launch. Look for leaks introduced by objects created from link parameters (e.g., lingering Bitmap objects from image URLs). Use MAT to identify any retained size >2 MB that is not released on subsequent navigation.

Battery and CPU usage

Run a script that fires a deep link every 30 seconds for 10 minutes while monitoring adb shell dumpsys batterystats. Ensure that the wake lock is not held longer than necessary and that the CPU usage spikes are bounded (<150 ms of CPU per link). Excessive wake locks can drain battery when users interact with links from notifications or widgets.

Network overhead

If the deep link triggers an API call (e.g., to fetch product details), capture the request size and response time with adb shell tcpdump or Charles Proxy. Verify that the app uses appropriate caching headers (ETag, Cache-Control) and that redundant calls are avoided when the same link is tapped repeatedly within a short interval.

Release Readiness

Versioning and backward compatibility

Maintain a matrix of supported deep link formats across app versions. When introducing a new parameter (e.g., campaignId), ensure older builds ignore it gracefully rather than treating it as an error. Test by installing an older APK, launching a link with the new parameter, and confirming the app does not crash.

Fallback to web content

If the app cannot handle a link (unsupported scheme, missing feature, or user has disabled deep links), provide a graceful web fallback. Test by disabling the intent filter temporarily (or using adb shell pm disable com.example.myapp/.DeepLinkActivity) and verifying that the URL opens in Chrome/Safari with the correct web page. Track the fallback rate in analytics to detect misconfigurations.

Monitoring and alerting

Instrument deep link handling with custom events: deep_link_received, deep_link_error, deep_link_fallback. Set up dashboards to monitor error rates per version and per link pattern. Alert if error rate exceeds 1 % for a given link type over a 5‑minute window. Example Firebase Analytics snippet:


Bundle params = new Bundle();
params.putString("link", uri.toString());
FirebaseAnalytics.getInstance(this).logEvent("deep_link_received", params);

Documentation and developer onboarding

Maintain a centralized deep link registry (e.g., a Confluence page or markdown file) that lists every supported pattern, required parameters, example URLs, and expected outcome. Include a validation script that CI can run against the registry to detect orphaned or duplicate entries.

Release checklist integration

Add the following items to your release sign‑off checklist:

Autonomous Exploration with SUSA

SUSA (SUSATest) can execute most of the items above in a single exploratory run without writing explicit test scripts. By pointing the agent at an APK or a web URL and enabling the Deep Link plugin, SUSA will:

  1. Discover registered schemes from the manifest (AndroidManifest.xml) or apple-app-site-association.
  2. Generate a matrix of variations for each discovered link: it injects random query parameters, omits required ones, adds excessively long values, and inserts Unicode/emoji strings.
  3. Execute each variant on a device farm or local emulator, capturing launch time, UI state, accessibility focus, and any logs.
  4. Detect crashes, ANRs, and unhandled exceptions via automatic logcat monitoring and symbolicating stack traces.
  5. Validate accessibility by running TalkBack/VoiceOver scripts after each launch and checking focus order and announcements.
  6. Check for open redirects by observing network calls; any request to a non‑whitelisted domain triggers a finding.
  7. Measure performance using built‑in timers (cold/warm start) and memory snapshots after each navigation.
  8. Produce a summary report that maps each finding back to the checklist item (happy path, error handling, etc.), making it trivial to export a CSV for test management.

To run SUSA locally:


pip install susatest-agent
susatest run --apk myapp-release.apk \
    --deep-link-scheme myapp \
    --max-parameter-length 2500 \
    --include-unicode \
    --output-format json \
    --out deeplink_report.json

The generated JSON contains entries like:


{
  "test": "deep_link_malformed_scheme",
  "input": "myapp%%%://home",
  "result": "crash",
  "stacktrace": "java.lang.NullPointerException: Attempt to invoke virtual method ...",
  "severity": "high"
}

Teams can import this report into their test management tool and close the loop between exploratory testing and scripted regression.

Manual vs Automated Approaches

Manual test matrix

For exploratory or ad‑hoc validation, a simple spreadsheet works well:

Test IDDeep linkExpected outcomeManual stepsPass/FailNotes
H1myapp://homeOpens home screenTap link from Notes app
E2myapp://profile?token=Redirects to loginPaste link in Chrome, tap OpenToken missing
E5myapp://home?redirect=evil.comNo network call to evil.comMonitor with CharlesBlocked
A3myapp://settingsFocus lands on Settings headingEnable TalkBack, swipeFocus on background

Manual testing excels at catching UX‑specific issues (e.g., confusing wording after a deep link) that automated assertions might miss.

Automated UI tests with Appium / Playwright

Encode the happy‑path and critical error cases as code. Example Appium Java test for a parameterized deep link:


@Test
public void testDeepLinkWithProductId() {
    String url = "myapp://product?productId=42&qty=1";
    driver.executeScript("mobile: deepLink", ImmutableMap.of("url", url));
    WebElement qty = driver.findElement(By.id("product_qty"));
    assertEquals("1", qty.getAttribute("text"));
}

Playwright equivalent for a web‑based universal link:


test('universal link opens app', async ({ page }) => {
    await page.goto('https://myapp.com/checkout?item=abc');
    await expect(page.locator('#checkout-button')).toBeVisible();
});

Integrate these tests in CI (GitHub Actions, GitLab CI) using the susatest-agent Docker image to run the exploratory suite alongside the scripted suite, ensuring both breadth and depth.

Combining both

Run the SUSA exploratory pass nightly to surface regressions in edge cases. Gate merges on the scripted happy‑path suite (fast, reliable) and require manual sign‑off for any new high‑severity finding from SUSA. This hybrid approach reduces flaky tests while keeping coverage high.

Short Checklist for Quick Reference

AreaItemPass criterionAutomation tip
Happy pathScheme launches correct activityStatus: ok & expected activityadb shell am start -W
Happy pathRequired parameters parsedUI reflects valuesData‑driven Appium
Happy pathOptional path ignoredNo crash, same screenVary path length
Error handlingMalformed URINo crash, fallback messageInject bad chars
Error handlingMissing required paramRedirect to login or errorOmit param
Error handlingUnsupported schemeBrowser fallback or errorTest alternate scheme
EdgeURL >2000 charsGraceful truncation or errorGenerate long string
EdgeUnicode/emojiCorrect decoding & displaySend encoded chars
EdgeDeep link chainEach hop succeeds, no loopChain 2‑3 links
EdgeTrailing slash/caseNormalized to lowercase, no slashTest variants
AccessibilityInitial focusFirst focusable gets TalkBack/VoiceOverRun accessibility script
AccessibilityPre‑filled announcementScreen reader speaks valueCheck spoken output
AccessibilityTouch target ≥48dp/44ptNo scanner violationsUse Accessibility Scanner
SecurityOpen redirect blockedNo request to external domainWhitelist check
SecurityParameter injection neutralizedNo unexpected DB queryUse payloads
SecurityApp link verification enforcedLink only opens app on verified domainTest unverified domain
PerformanceCold start <1500 msMeasure with -WCapture TotalTime
PerformanceWarm start <800 msCompare to cold startHome then relaunch
PerformanceNo memory leak >2 MBHeap diff baselineMAT analysis
ReleaseBackward compatibilityOld build ignores new paramsTest with older APK
ReleaseWeb fallback worksLink opens in browser when disabledDisable intent filter
ReleaseAnalytics events firedeep_link_received loggedVerify with Firebase

Keep this table in your test plan repository; tick items off as they are automated or verified manually.

Takeaways

Deep links remain a critical user‑acquisition and re‑engagement vector in 2026, and their complexity continues to grow with new operating system features, stricter privacy controls, and richer parameter schemas. A structured checklist—like the one presented above—turns an otherwise ad‑hoc verification effort into a repeatable, measurable process.

Autonomous exploration platforms such as SUSA can cover the majority of these items in a single pass, generating actionable reports that feed directly into both manual test sessions and automated regression suites. By combining the breadth of an exploratory run with the precision of scripted happy‑path tests and occasional manual spot‑checks, teams achieve confidence that deep links will behave correctly for every persona—curious, impatient, novice, adversarial, elderly, accessibility‑aware, power user, and beyond—while keeping the feedback loop short and the signal‑to‑noise ratio high.

Adopt this checklist, integrate it into your CI, and let your QA process evolve alongside the deep links that power your app’s growth.

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