WebdriverIO with Appium: Beyond the perfect scenario

Marcelo Soares
Zenjob Technology Blog
5 min readNov 4, 2022

--

Have you ever tried a new tool or framework that didn’t initially work as expected? Did your scenario differ from the documentation description? If so, you’re not alone; it’s happened to us too!

Here at Zenjob, we used to spend a lot of time testing our apps manually on every release, running massive end-to-end (E2E) test scenarios, and redoing the tests after fixing issues we encountered. But all this work took a lot of manual effort, so instead, we started automating the E2E tests.

We needed a testing framework for this, so we picked WebdriverIO, mainly because it was already in use for some of our E2E web tests. As a result, we assumed the learning curve would be gentler. Additionally, it’s a well-known tool with support from the developer community, it has good documentation, and it fits in nicely with our JavaScript/TypeScript stack.

We use it together with Appium, which is a test automation framework for mobile apps that makes use of the WebDriver protocol.

Automating for both platforms at once is always challenging — Photo by AltumCode on Unsplash

Glossary

Before getting into the details of what we did, it might first help to define a couple of terms.

Elements are parts of a user interface (UI) that you can see and interact with. Examples of elements include text boxes, buttons, labels, and checkboxes.

Attributes are properties of UI elements. These can be used as selectors to identify and interact with elements — for example: class, ID, accessibility ID, and type.

Default attributes

The WebdriverIO documentation is well written and complete, but for cross-platform mobile testing, there’s an expectation that you should have accessibility IDs — which are unique identifiers — in your apps, so that you can use the same code for both platforms:

All the elements you’ll interact with need to have the same value on both platforms if you want them to work with the same code.

So, assuming there’s a button to submit a form in an iOS app, it’s:

submitButton.accessibilityIdentifier = “submit"

Meanwhile, for Android, it’s:

android:contentDescription="@string/submit"

With this “perfect scenario” of both platforms having attributes with the same value, you’d find elements like this:

Two lines of code working for both platforms! Easy, right?

While this is ideal, it doesn’t always translate to reality, and since our apps differ from what’s outlined in the WebdriverIO docs (more on this in the next section), we had to find a workaround.

The reality

In our current repositories, we have the following:

  • In the iOS app, most elements have an accessibility-identifier.
  • In the Android app, most elements have a resource-id.

Here, each development team uses identifiers for its respective operating system (OS). As a result, they’re not the same (e.g. we can have signinButton and button-login for the same element in different apps).

We could go through each app’s code and add identifiers to all elements to ensure they’re identical in both apps. However, sometimes the architecture of each app makes it difficult to change an attribute of a specific element, and refactoring would require a more extensive effort.

What can we do instead?

In this scenario, the options were to either use something common for both apps — which would be the text on each element — or provide a way to differentiate elements for each OS, using the same object in the code so that we’d only have to write the code once.

Using text as an identifier isn’t recommended on UI tests. This is because it constantly changes due to product and user experience (UX) requirements and makes you spend time refactoring the test code due to word changes. Because of this, we chose to use a “shared” object with the element locators.

First, we needed a helper function to enable us to find elements in each app using the different attributes — resource-id for Android and accessibility identifier for iOS:

Calm down, code reviewers. It’s just a piece of readable code.

With the above set, we started thinking of using an object with the locators for each OS without needing to duplicate test code.

As you can see above, the driver has a property, isAndroid, (and it also has an isIOS property), which uses the platformName from the configuration file to check which OS the tests are running on. We created two locator objects — androidLocators and iOSLocators — with the same attributes, and we picked one based on the running OS (line 13), as shown in the example below (we use page objects):

Page Object Model (POM) example for a sign-in screen.

With this, we could achieve our goal of running the same code for both apps, and all the page classes have two JSON objects containing the locators for each OS.

Is this the best way?

No. The best way would still be to do what’s described in the first part of this article and have the same value as accessibilityIdentifier on iOS and content-description on Android. You wouldn’t need the previously mentioned helpers or objects and could simply write the getters using $(‘~my_identifier’). If this is your scenario, you just need to read the documentation, and you’re all set.

But, as we know, things aren’t always perfect, and sometimes we need to work with the tools we have instead of waiting for someone else to give us better ones.

When you see an identifier missing, you can just add it into the code, but in our case, for the Android app, there were some blockers to adding the content-description for some elements. So instead, we adapted our test code to use the attributes we already had set.

What’s shown in the documentation as the simpler scenario might not work for everyone’s code, and this post outlines one way to deal with the problems you may face in your daily job. When automating tests, sometimes you may not even have proper access to the application code, and you’ll need to find a way to unblock yourself and automate your tests, so workarounds like this are great, and it sure beats having to test everything manually!

If you’re interested in what we’re doing at Zenjob, you can read more on our blog.

If you want to read my other posts, check out my profile.

--

--

Marcelo Soares
Zenjob Technology Blog

QA Engineer @ Zenjob GmbH. Brazilian living in Berlin. QA since 2010, trying to help people to deliver quality software.