Unlocking Webdriver.IO framework’s potential for two-device mobile automation

Enes Kuhn
Maestral, an HTEC Group Company
5 min readNov 18, 2022

We all know how “fun” mobile automation can be — and it only gets more fun when you’re working on a multi-device test automation scenario. It’s why it’s important to have the right tools available.

One of those tools is the Webdriver.IO (WDIO) framework. Chances are you’ve either considered using it or are already using it for your mobile automation project. Recently, my team had the opportunity to utilize it for an advanced two-device mobile test scenario automation. In this blog post, I will demonstrate how we went about it.

What is WDIO?

Before we get into it, let’s pedal back and cover what WDIO is and what it does. According to their official site:

WebdriverIO is a progressive automation framework built to automate modern web and mobile applications. It simplifies the interaction with your app and provides a set of plugins that help you create a scalable, robust and stable test suite.

It is designed to be:

  • Extendable — Adding helper functions, or more complicated sets and combinations of existing commands is simple and really useful
  • Compatible — WebdriverIO can be run on the WebDriver Protocol for true cross-browser testing as well as Chrome DevTools Protocol for Chromium based automation using Puppeteer.
  • Feature Rich — The huge variety of built-in and community plugins allows you to easily integrate and extend your setup to fulfill your requirements.

Aside from the above features, for me personally, the best thing is the fact that you can easily scale your test automation framework both horizontally and vertically. Horizontal scalability refers to utilizing multiple instances of the same platform, such as multiple web browsers; and vertical scalability is the ability to support different platforms within a single framework (web, mobile, API, etc.).

Unleashing the beast

So, what led me to the idea of using WDIO on my project?

My team needed to automate a business critical flow that was a simple native android and iOS application used for secure messaging and calling, similar to WhatsApp, Viber, Messenger etc. Aside from the native app, we also have a web application that provides the same functionality and has a lot more features, such as appointment scheduling, call rates, feedback management, log tracking, etc.

In order to achieve a high test automation coverage of existing manual test cases, such as call from android to iOS and vice versa, call from web to android/iOS and vice versa, the best, and simply the only option, for us is to choose the WDIO framework as it provides just what we need.

Unfortunately, it is truly difficult to find an online solution for a case where two devices are communicating independently in a single test case. The online documentation is mostly focused to cross-device testing where two devices are used to execute the same test case but under different configuration. This meant that we had to work through creating a solution through trial and error.

After a few days of failed attempts and several tweaks, we finally managed to achieve what we wanted. The process is laid out in the steps below.

Step #1: wdio.conf.js update

Update the services and capabilities properties in a way to make the Appium service run on a unique port for all devices. In our case, we have two android devices, so we made our config as follows.

services:
[
['appium', {
command: 'appium',
args: {
port: 4444
},
}],
['appium', {
command: 'appium',
args: {
debugLogSpacing: true,
sessionOverride: true,
port: 4445
},
}]
],

We also needed to update the capabilities property and to give a property name for both devices.

capabilities: {
phone1: {
port: 4444,
path: '/',
capabilities: {
platformName: 'android',
'appium:platformVersion': '11',
'appium:adbExecTimeout': 50000,
'appium:automationName': 'UiAutomator2',
'appium:avd': '<PHONE1_NAME>',
"appium:app": '<APP_LOCATION>',
...<OTHER_CAPABILITY_SETTINGS>
}
},
phone2: {
port: 4445,
path: '/',
capabilities: {
platformName: 'android',
'appium:platformVersion': '10',
'appium:adbExecTimeout': 50000,
'appium:automationName': 'UiAutomator2',
'appium:avd': '<PHONE2_NAME>',
"appium:app": '<APP_LOCATION>',
...<OTHER_CAPABILITY_SETTINGS>
}
},
},

Step #2: Within page object classes, move the element locators to the constructor

module.exports = class MyPage {constructor(device = phone1) {
this.inputField = e => device.$('android=.resourceId("<RESOURCE_ID>")');
this.someButton = e => device.$('~<ACCESSIBILITY_ID>');
this.weirdLabel = e => device.$('//<WEIRD_XPATH>');
}
async doSomethingCool(inputText){
await this.inputField().setValue(inputText);
await this.someButton().click();
}
}

This step is extremely important for distinguishing elements between devices. There are other ways to achieve it for sure, but I find this simple and easy.

Step #3 Migrate from the WDIO built-in assertion to Node.js assert

To date, the WDIO built-in assertion package has limitations when dealing with two devices within a single test. It usually only accepts a single object. With two active devices, it gets an array of objects and fails every time. The easiest way to fix the issue is to simply include an assert package.

import assert from 'assert';module.exports = class MyPage {constructor(device = phone1) {
this.inputField = e => device.$('android=.resourceId("<RESOURCE_ID>")');
this.someButton = e => device.$('~<ACCESSIBILITY_ID>');
this.weirdLabel = e => device.$('//<WEIRD_XPATH>');
}
async doSomethingCool(inputText){
assert.equal(await this.inputField().isDisplayed(), true);
assert.equal(await this.someButton().isDisplayed(), true);
assert.equal(await this.weirdLabel().isDisplayed(), true);
await this.inputField().setValue(inputText)
await this.someButton().click();
}
}

Step #4 Update spec files and run the test

Create two page object instances with two different capabilities and run the test.

const MyPage = require("../<MY_LOCATION>/MyPage");let myPageFirstDevice, myPageSecondDevice;describe("Demo", () => {
before(async () => {
myPageFirstDevice = new MyPage(phone1); //phone1 - first device capability name
myPageSecondDevice = new MyPage(phone2); //phone2 - second device capability name
});
it('My First Test', async () => {
await myPageFirstDevice.doSomethingCool('device #1 test');
await myPageSecondDevice.doSomethingCool('device #2 test');
});
});

Running the tests on cloud

fter a successful automation process, the next natural step would be to run the tests as a part of CI/CD pipelines or to create scheduled nightly runs. For both options we need to have available devices for test case execution. Luckily, we have dozens of cloud device farm options available. Or so it would seem.

Unfortunately, when you look at the available license details, most services are tightened to single device = single test. Except for one: the BrowserStack services. The main problem with other service providers is device availability. However, the BrowserStack App Automate 2 parallel test license handles those two device scenarios out of the box. We utilized it for our automation project, and it proved to be highly effective.

Accomplishing the (im)possible

While it may have seemed to be mission impossible to create a two-device mobile test scenario, we worked diligently to overcome the obstacles in our way. With the WDIO framework and a few adjustments here and there, a solution was uncovered within days, allowing us to achieve the test automation coverage we sought. I hope the solution I’ve laid out here proves to be helpful should you find yourself in a similar situation.

Special thanks to…

Nikolina Djekic (Amplitudo doo) — for believing in the webdriver.io multidevice automation scenario idea and transforming the idea into code

Chinmay Patil (BrowserStack) — for providing awesome customer assistance and helping us out with licensing options and opportunities

--

--