Deep Linking — Shortcut for Faster Appium Tests

Lana Begunova
8 min readNov 23, 2023

Deep Link Technique — a cross-platform strategy to speed up mobile test execution and minimize flakiness.

In the realm of mobile software testing, functional tests are one of the biggest challenges because they can be slow. We can enhance automation speed by building shortcuts into our Appium testing.

What do we mean by shortcuts? Let’s take logging in to an app as an example. Most apps require some kind of login step. Before our tests can access all the functionality they’re supposed to exercise, they must first log in. And if we keep your tests atomic, as we should, every test has to log in separately. This is a huge waste of time. We only need to run Appium commands on our login screen when you are actively testing the login functionality. For every other test, it’d be great to just land somewhere behind the authentication barrier and get directly to business.

We can accomplish this on iOS and Android with a technique called Deep Linking. This is a cross-platform OS-level strategy, whereby clicking on a URL with a custom scheme (i.e., a vendor-specific scheme which differs from “http” or “https”) actually takes us to our application. The URL is passed to the app, and thus the app can parse it for all kinds of data which then direct the user experience.

Deep links are special URLs that can be associated with specific apps. The developer of the app registers a unique URL scheme with the OS, and from then on, the app comes alive and is able to do whatever it wants based on the content of the URL.

Custom URL scheme registered with a mobile OS.

Deep links are essentially URLs with a custom URL scheme that our app has registered with the mobile operating system. These custom URL schemes can be registered on both Android and iOS, as part of the application configuration or manifest file.

Mobile OS opens the app, passing link content.

Suppose that our app has registered one of these custom schemes. Then, when a user tries to navigate to a URL beginning with our scheme, the mobile device will not try and load a web page with that URL, instead our app will be opened, and it will be given the content of the link, whatever it is.

Links can be parsed using an arbitrary format.

Now that our app is open, and has this link content, the app can parse this link content however it wants, just like a web server can parse a URL however it wants to support a REST API that has a certain format.

Action taken based on link parsing.

So basically, any kind of data can be put into, and then extracted from, a URL in this way. Once that data is parsed and extracted, our app can take any action we define, based on this data. In our case, the action would be to set up some kind of app state, or navigate somewhere in the app, that would normally take a long time using the UI.

Let’s take a look at a simple example. We could have a URL that looks like this: theapp://login/alice/mypassword.

A special URL that our app interprets as a login attempt. When it detects that it has been activated with that URL, it will perform the login behind the scenes and redirect the app to the correct logged-in view.

This URL doesn’t start with http, or https, instead it starts with theapp. This is the custom scheme that the app has registered with the mobile OS.

As for the rest of the URL, this is what we would call the path for the URL. Notice that it starts with login, then has a placeholder for a username and a password, separated by a slash /. This is the way that the developer chose to encode a login action with username and password parameters. Of course, they could have used, query parameters, or any other format instead.

By following this format, we construct a URL like the one we have here: theapp://login/alice/mypassword. When we try to navigate to this URL on the device, the app gets launched. The app parses this URL for the user login information and then immediately logs the user in.

When clicked, the URL would open up our application in a state where the login verification has already been performed with the supplied username and password. Using this deep link means we get to bypass the entire login process in the UI, but we can still login as any user that we want, by passing in the user credentials via this deep link URL.

Deep Links in Appium

The one question left is how to click on a deep link. How do we trigger the URL to launch directly in Appium? Assuming our app has registered the custom URL scheme with the OS, all we have to do is call driver.get, with the URL we want.

Since the Appium client libraries just extend the Selenium ones, we can use the same command that Selenium would use to navigate to a browser URL. In our case, the concept of URL is just a bit more expanded.

Let's say we want to log in with the username alice and the password mypassword. All we have to do is construct a test that opens a session with our app, and runs the following command:

driver.get("theapp://login/alice/mypassword")

In other words, with our app installed on a device, we can direct the device to navigate to a URL with that form, and the app will wake up and perform authentication of the requested username/password combination behind the scenes, dumping the user to the logged-in area instantly.

Of course, it’s up to the app developers to build the ability to respond to URLs and perform the appropriate action with them. And being able to set up arbitrary test state is the best way to cut off significant portions of time off our testing. We can save up to 5–10 seconds on each test just by bypassing the login screen in this way ⏳. These savings accumulate over time 💰.

Practical Example

Let’s have a quick look at deep links in action. Because we’ve already looked at the single Appium command necessary for this to work, let’s review how deep links can really simplify and speed up an Appium test.

Notice that because this is a cross-platform React Native app, we are able to run these tests on both iOS and Android with minimal code difference.

We have two test cases in Java here. One is called testLoginNormally. It consists of the steps required to login as a certain user within the app, by clicking and typing in the UI. It's about 20 lines of code.

@Test
public void testLoginNormally() {
WebDriverWait wait = new WebDriverWait(driver, 10);

WebElement screen = wait.until(ExpectedConditions.presenceOfElementLocated(AppiumBy.AccessibilityId("Login Screen")));
screen.click();

WebElement username = wait.until(ExpectedConditions.presenceOfElementLocated(AppiumBy.AccessibilityId("username")));
username.sendKeys("alice");

WebElement password = driver.findElement(AppiumBy.AccessibilityId("password"));
password.sendKeys("mypassword");

WebElement login = driver.findElement(AppiumBy.AccessibilityId("loginBtn"));
login.click();

WebElement loginText = wait.until(ExpectedConditions.presenceOfElementLocated(
AppiumBy.xpath("//android.widget.TextView[contains(@text, 'You are logged in')]")));

assert(loginText.getText().contains("alice"));
}

The test method below is called testLoginWithDeepLink, and it does the exact same thing as the previous test, only it uses a deep link to login, rather than using the UI. It makes the same verification, using the assert method at the end, but the test case is only 5–6 lines long.

@Test
public void testLoginWithDeepLink() {
driver.get("theapp://login/alice/mypassword");

WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement loginText = wait.until(ExpectedConditions.presenceOfElementLocated(
AppiumBy.xpath("//android.widget.TextView[contains(@text, 'You are logged in')]")));

assert(loginText.getText().contains("alice"));
}
}

The same test can be replicated in Python as follows:

driver.get("theapp://login/alice/mypassword")
wait = WebDriverWait(driver, 10)
login_text = wait.until(EC.presence_of_element_located(
(AppiumBy.XPATH, "//android.widget.TextView[contains(@text, 'You are logged in')]")))
assert "alice" in login_text.text

I’ve prepared Python file deep_link_android.py for the demo. Let’s run it in the terminal with the python3 command:

python3 deep_link_android.py 
HD Video: https://youtu.be/Gx9BdKZd-Ec

You can imagine that if we had many test cases, that all involved logging in at the beginning, having just one command here would save us lots of time, as well as lots of potentially duplicated code.

Pros ➕and Cons ➖

A Deep Link URL can contain arbitrary content. One benefit of it is that custom APIs can be developed using Deep Link URLs, allowing for parameters to be passed in. Since URLs are just a bunch of text, we can design a full-fledged API, including passing in parameters to certain methods.

Let’s review the pros and cons of the deep link technique.

Used for testing, Deep Linking is a powerful way to set up a very flexible URL structure that can be used to direct our test to anywhere we want in our app. It does require the app developer to have registered a custom scheme with the OS, and to have created some kind of URL controller/handler that parses the URL and actually navigates the app to the correct view with the appropriate state.

Conclusion

That’s a wrap on this technique. All we need to do for deep links is build in the deep link API on the app side, and it has the potential to make our testing life quite a bit easier.

Faster execution. Less code.

Cheers to speedier automated testing! Stay tuned for upcoming posts about making mobile test suites faster, more reliable, and less flaky.

I welcome any comments and contributions to the subject. Connect with me on LinkedIn, X , GitHub, or Insta.

🔗 Extra Resources

--

--

Lana Begunova

I am a QA Automation Engineer passionate about discovering new technologies and learning from it. The processes that connect people and tech spark my curiosity.