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
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 set | Expected screen | Pre‑filled fields | Pass/Fail |
|---|---|---|---|
?section=profile | Profile tab | None | ✅ |
?section=settings&tab=notifications | Settings → Notifications | None | ✅ |
?productId=12345&qty=2 | Product detail | Qty = 2 | ✅ |
| (empty) | Home screen | None | ✅ |
?invalid=foo | Home 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:
- [ ] All happy‑path deep links pass automated UI tests on Android emulators (API 21‑33) and iOS simulators (13‑17).
- [ ] Error‑handling matrix shows no crashes or ANRs.
- [ ] Accessibility scan reports zero new violations after deep‑link navigation.
- [ ] Security scan (MobSF / OWASP ZAP) flags no open‑redirect or injection issues.
- [ ] Performance benchmarks meet defined thresholds (cold start <1500 ms, warm start <800 ms).
- [ ] Fallback to web works when deep link is disabled.
- [ ] Analytics events fire correctly for success and error paths.
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:
- Discover registered schemes from the manifest (
AndroidManifest.xml) orapple-app-site-association. - 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.
- Execute each variant on a device farm or local emulator, capturing launch time, UI state, accessibility focus, and any logs.
- Detect crashes, ANRs, and unhandled exceptions via automatic logcat monitoring and symbolicating stack traces.
- Validate accessibility by running TalkBack/VoiceOver scripts after each launch and checking focus order and announcements.
- Check for open redirects by observing network calls; any request to a non‑whitelisted domain triggers a finding.
- Measure performance using built‑in timers (cold/warm start) and memory snapshots after each navigation.
- 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 ID | Deep link | Expected outcome | Manual steps | Pass/Fail | Notes |
|---|---|---|---|---|---|
| H1 | myapp://home | Opens home screen | Tap link from Notes app | ✅ | — |
| E2 | myapp://profile?token= | Redirects to login | Paste link in Chrome, tap Open | ✅ | Token missing |
| E5 | myapp://home?redirect=evil.com | No network call to evil.com | Monitor with Charles | ✅ | Blocked |
| A3 | myapp://settings | Focus lands on Settings heading | Enable TalkBack, swipe | ❌ | Focus 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
| Area | Item | Pass criterion | Automation tip |
|---|---|---|---|
| Happy path | Scheme launches correct activity | Status: ok & expected activity | adb shell am start -W |
| Happy path | Required parameters parsed | UI reflects values | Data‑driven Appium |
| Happy path | Optional path ignored | No crash, same screen | Vary path length |
| Error handling | Malformed URI | No crash, fallback message | Inject bad chars |
| Error handling | Missing required param | Redirect to login or error | Omit param |
| Error handling | Unsupported scheme | Browser fallback or error | Test alternate scheme |
| Edge | URL >2000 chars | Graceful truncation or error | Generate long string |
| Edge | Unicode/emoji | Correct decoding & display | Send encoded chars |
| Edge | Deep link chain | Each hop succeeds, no loop | Chain 2‑3 links |
| Edge | Trailing slash/case | Normalized to lowercase, no slash | Test variants |
| Accessibility | Initial focus | First focusable gets TalkBack/VoiceOver | Run accessibility script |
| Accessibility | Pre‑filled announcement | Screen reader speaks value | Check spoken output |
| Accessibility | Touch target ≥48dp/44pt | No scanner violations | Use Accessibility Scanner |
| Security | Open redirect blocked | No request to external domain | Whitelist check |
| Security | Parameter injection neutralized | No unexpected DB query | Use payloads |
| Security | App link verification enforced | Link only opens app on verified domain | Test unverified domain |
| Performance | Cold start <1500 ms | Measure with -W | Capture TotalTime |
| Performance | Warm start <800 ms | Compare to cold start | Home then relaunch |
| Performance | No memory leak >2 MB | Heap diff baseline | MAT analysis |
| Release | Backward compatibility | Old build ignores new params | Test with older APK |
| Release | Web fallback works | Link opens in browser when disabled | Disable intent filter |
| Release | Analytics events fire | deep_link_received logged | Verify 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.
- Happy‑path validation ensures the core navigation works as advertised.
- Error handling and edge cases protect against crashes that would otherwise only surface in the wild.
- Accessibility checks guarantee that users relying on assistive technologies receive the same experience.
- Security and privacy safeguards prevent open redirects, injection attacks, and inadvertent data leakage.
- Performance benchmarks keep launch times within user‑acceptable thresholds, especially for links launched from notifications or widgets.
- Release readiness items (versioning, fallback, monitoring) make sure that each ship‑to‑production step is auditable and observable.
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