Deep link testing is where I have found some of the most embarrassing mobile bugs: links that open the store page for an app that is already installed, links that dump logged-out users on a blank screen, back buttons that exit the app from three levels deep. Deep links cross the boundary between the operating system and the app, and boundaries are where QA earns its keep. Here is how I structure deep link testing for mobile apps.
Universal links, app links, and custom schemes
Custom schemes like myapp://item/42 are easy to test but unverified — any app can claim a scheme. Universal links on iOS and app links on Android are https URLs verified against a file hosted on the domain: apple-app-site-association and assetlinks.json. Check that those files actually cover the paths the product uses, because a path missing from the association file falls back to the browser silently, with no error anywhere. Most products ship both link types, so test both.
Cold start versus warm start
A warm app just navigates. A cold start has to initialize everything — dependency graph, session restore, splash flow — and then remember to route. Test every important link with the app killed, backgrounded, and foregrounded. Cold start is where links get lost: the app launches, plays its splash animation, and lands on the home screen, having forgotten why it was opened at all.
Links behind the login wall
Open a link that targets an authenticated screen while logged out. The right behavior is to hold the destination, run the login flow, then continue to the original target. The common bug is dropping the link on the login screen floor. Test the reverse too: a session that expired server-side but still looks logged in until the first API call fails.
Malformed and expired links
Attack your own links: missing IDs, garbage parameters, IDs pointing at deleted content, expired campaign links. None of these should crash or strand the user; each should land on a sensible fallback with a clear message. Deleted content is the case teams forget — shared links and old notifications outlive the things they point to.
Back-stack behavior
After deep-linking three screens into the app, press back. A well-behaved app synthesizes a back stack so the user lands on a parent screen or home. A broken one exits straight to the launcher, or loops back into the deep-linked screen forever. Agree on the expected stack with the team before testing — this is a spec question as much as a QA one.
A deep link testing matrix
I test links as a matrix across four dimensions: source (browser, email client, chat app, QR code, notification), app state (not installed, killed, background), auth state (logged in, logged out, expired), and link validity (valid, malformed, expired target). Most cells take seconds with the right commands:
adb shell am start -a android.intent.action.VIEW \
-d "https://example.com/item/42" com.example.app
xcrun simctl openurl booted "https://example.com/item/42"
The "not installed" column matters more than it looks: it covers deferred deep links and the store handoff, and it is the one state you cannot reach without actually uninstalling. I keep one device in the matrix permanently app-free for exactly this reason.
Deep links are a small feature with a huge surface. Enumerate the matrix once, automate the cheap cells — launching a URL and asserting the landing screen is a natural fit for a Maestro flow in CI — and rerun the whole thing every time routing code changes. Which, in my experience, is far more often than anyone admits.