End to end (e2e) cross platform testing for your mobile apps with Appium

In this post I will talk about how to use Appium to write cross platform end to end tests for you mobile apps.

During my daily job I’m used to write unit test for my code. In fact, I usually develop using Test Driven Development technique. Anyway at the end of the development of a new feature you want to be sure that the entire system works as expected. In particular for a mobile developer, you want to test the entire new feature flow inside your app. This is usually what is called end to end test. In the last few months the mobile team “Team Cook” at lastminute.com group, of which I’m a member, decided to try an end to end testing infrastructure for the mobile apps of our main brand lastminute.com, volagratis and rumbo. In this post I will described this testing infrastructure and how it works.

Software

To put in place the e2e infrastructure we chose:

Development

The first thing we did was installing all the software stack previously described on our CI machine. As a consequence of the fact that we want to run tests for both iOS and Android a macOS based CI machine is needed (because you need to install Xcode). Fortunately, our CI machine was already an Apple Computer so we didn’t need to change anything.
After that we created a new javascript project, that follows the structure of the WebdriverIO sample code contained in the Appium github repository. This sample project is written using ES5 syntax, so we decided to upgrade it to use ES6 syntax and compile it using Babel. This is possible by launching mocha and specifing babel as the compiler. This is the final command to launch our tests:

mocha --compilers js:babel-core/register --timeout 6000000 test

This is the final package.json with all the dependencies and scripts phases.

{
"name": "e2e-tests",
"version": "1.0.0",
"description": "e2e tests",
"main": "index.js",
"scripts": {
"pretest": "./download-artifacts.sh",
"test": "mocha --compilers js:babel-core/register --timeout 6000000 test"
},
"author": "Fabrizio Duroni",
"license": "MIT",
"devDependencies": {
"assert": "^1.4.1",
"babel-core": "^6.26.3",
"babel-preset-env": "^1.7.0",
"babel-plugin-transform-runtime": "^6.23.0",
"chai": "^4.1.2",
"mocha": "^5.0.0",
"webdriverio": "^4.12.0"
}
}

This is the .babelrc file used to configure the babel compiler. The transform-runtime plugin is needed in order to be able to use arrow functions.

{
"presets": ["env"],
"plugins": [
["transform-runtime", {
"polyfill": false,
"regenerator": true
}]
]
}

As you may already notice from the package.json file above, there’s a pretest phase that launches a script called download-artifacts.sh. This a custom script we added to download the latest release of our iOS ipa and Android apk artifacts. This will be the apps installed on the iOS simulators/Android emulators and tested with appium.
 After that we created the iOS and Android appium config to be used by our tests.

import path from "path";
const iOSConfig = {
protocol: "http",
host: "localhost",
port: 4723,
path: "/wd/hub",
logLevel: "verbose",
desiredCapabilities: {
platformName: "iOS",
automationName: "XCUITest",
deviceName: "iPhone 8",
platformVersion: "11.4",
clearSystemFiles: true,
wdaStartupRetryInterval: 1000,
useNewWDA: true,
waitForQuiescence: false,
shouldUseSingletonTestManager: false,
app: path.resolve(__dirname, "..", "apps", "<ipa downloaded using pretest download.sh script>"),
orientation: "PORTRAIT",
}
};
const androidConfig = {
host: "localhost",
port: 4723,
logLevel: "verbose",
desiredCapabilities: {
platformName: "Android",
automationName: "UiAutomator2",
deviceName: "Pixel_XL_API_27",
platformVersion: "8.1",
app: path.resolve(__dirname, "..", "apps", "<apk downloaded using pretest download.sh script>")
}
};
export {iOSConfig, androidConfig}

One important thing to note about the configuration above is that for iOS we were forced to set the following four options:

wdaStartupRetryInterval: 1000,
useNewWDA: true,
waitForQuiescence: false,
shouldUseSingletonTestManager: false,

These were need in order to avoid a know bug in appium for iOS that causes the appium test suite to get stuck during the creation of the appium WebdriverIO session. After the configuration setup we were ready to write our first tests. To write them we used the Appium desktop app to record the interfaction with our apps. The outcome of the recording is a test source code written in the language + driver you prefer (in our case JavaScript + WebdriverIO). Remember that appium uses the accessibility id on iOS and the content-desc on Android to unify the search method for both platform. If this fields are not setted correctly with the UI elements you interact with, the appium desktop app will generate a XPath queries for XCUITest or UiAutomator. This will cause you to write two tests with the same interaction just to change the UI elements identifier (or write some wrapper with parametrized UIElements). So the best solution to have appium works correctly is to set the fields above with the same values on both iOS and Android.
After that we launched the appium server on the CI machine previously configured and we created a new Jenkins job that clones the e2e-tests project and runs the command:

This job is autmatically triggered (cron) every day at 8 PM. That’s it!!! This is how we tested our apps with Appium. Tha’s all for appium and mobile end to end tests. If you have any question don’t hesitate to comment on this post below 💝.


Originally published at www.fabrizioduroni.it on February 7, 2019.