Issue 59

Mac OS API over Appium for testing cross-platform applications simultaneously

Paul Bodean
Lead Automation Engineer @ Telenav

Eugen Meltis
Automation Software Developer @ Telenav


A walkthrough of the key aspects of MAF - internal mobile automation API over Appium providing access to multiple devices on iOS and Android at runtime is described. The article covers some generic information about the Appium automation service, a personal challenge in the production environment (alongside the adopted solution) and a step-by-step tutorial covering the API installation and configuration.

Appium - general description

Appium is an open source test automation framework for mobile apps. Currently supported platforms are iOS, Android and Windows which make use of the WebDriver protocol. It is also cross-platform meaning that it allows you to write tests for the previously mentioned platforms based on the same API. It enables code reusability across platform test cases as long as the UI of the applications is similar. To this end, it leads to a well-structured architecture of your testing framework. Another advantage of Appium is the flexibility of choosing the programming language for developing test cases. The currently supported ones are Java, Ruby, Python, PHP, JavaScript, and C#.

A new layer

The problem

The main downside of Appium is that it is not currently designed to support multithreaded sessions, meaning that you cannot run the same tests on multiple devices at the same time.

The solution

Currently benefitting from the advantage of cross-platform functionality, a feature was developed for running the test cases concurrently on different physical devices.

General overview

The API is based on an abstract architecture consisting of a Client class with three main composition classes: TouchGestures, ElementValidation and ElementLogging, where each interaction class is connected to a Driver class that links the logic to the device.

To implement this solution three main aspects must be covered:


This section covers some aspects related to the installation and configuration of the API, the key actions that MAF API provides, leading up to a short presentation about the implementation of the tests. The current version supports automation on two platforms (Android and iOS) and it can be executed on Mac OS.


Initial setup - Make sure the initial setup contains the following requirements


Appium Version: 1.6.3

Mac OS: El Capitan

iPhone OS >= 10.0

Android SDK

Python 3


Appium installation - is required so please find the complete guide here


carthage: brew install carthage

brew uninstall libimobiledevice && brew install --HEAD ibimobiledevice

brew install ios-deploy

iOS related - the Xcode WebDriverAgent needs to be configured with some valid provisioning profiles. Since iOS 10, this project (application) acts as a bridge between your test cases and the actual application on the device.

Appium Server

Once the environment configuration is done, it is time to implement the tests specification. Usually, while using Appium, the first thing to do is to start the server through an external UI. To skip this step an API that handles these actions was developed.

appium_instance_ios = AppiumServer()
appium_instance_android = AppiumServer()
appium_instance_ios.start(port=9000) appium-python-client

This code snippet is the perfect example for showing how to make use of the same test on two different devices. The AppiumServer class represents the Python implementation of the Appium server API. Going further, an iOS instance and an Android instance is created, both of them calling the same start() method. The key point for running the test simultaneously on two devices is represented by the port parameter. In this case, for iOS, port:9000 is used, while, for Android, port :8000 is used.

Appium Driver

The driver class acts as a factory used by the client class implementation in order to establish the connection with the designated Appium server instance. Note that there is a one-to-one relation between a client instance and the server. The driver class makes use of the appium-python-client library which provides a web-driver instance in order to transmit the commands to the Appium service. First, a client calls a web-driver instance; the driver class creates the connection and maps the instance to the unique identifier of the client. In this way when the client requests another connection, the driver class provides the already mapped instance. In brief, the driver class manages singleton connections to Appium services.

Client implementations

Each custom client class contains the implementations for the three abstract utility classes: Gestures, Logger, and Validator corresponding to their specific driver, in this case, the appium-python-client driver. Besides the mandatory abstract methods, the classes can be extended with some custom client specific functions, e.g. if the text value of a certain element is required, then a method called "get_text_from" will be defined in the Logger implementation, since it handles information about the UI elements. If it is necessary to validate a text attribute of an element, a method called "validate_text" will be created in the Validator implementation since the method returns a True or False value.

Write tests

The @test_case("test_ID") decorator creates a dynamic link between the test data file and the test implementation using the test_ID as a matcher. Keeping test data in a separate file and structure relative to the unique test identifier provides an easy way to manage the changes in the long run. The data includes input values and expected reference data in order to validate/invalidate the test run. The data is provided through the mandatory method argument "kwargs" - a dictionary data structure. Access to this data is given via the 'test_data' key for the dictionary that the test is called with. Furthermore, an error handling structure is responsible for providing a crash-save flow.

Test case example

Considering everything mentioned before, here is a simple test flow as an example that performs some simple screen interactions.

def my_first_test(**kwargs):
# kwargs['test_data'] holds all the test specific data
    text_input = kwargs['test data']['input_text']
        # test case steps for android client
    appium_service_android = AppiumServer()
    android_client = AndroidClient()
    # test case steps for iOS client
    appium_service_iOS = AppiumServer()
    iOS_client = iOSClient()
         'input_box', text_input)
    # the data is send to the iOS application
    # check the iOS app if received the data.
    request_message = iOS_client.logger
    # validate the step
    assert iOS_client.validator
          .check_value('request_text', text_input)
        print("Test case ended with status: OK")
    except Exception as ex:
        print("Error executing test case: "
          + str(ex), file=sys.stderr)


Hours of work and research over Appium have proved that it is a reliable framework for client automation projects. The flexibility of choosing your favorite programming language, as well as the cross-platform functionality can make the continuous delivery process an easy job for any developer. In the Telenav context, now implemented, the currently described API saved hours of manual testing effort and also introduced the easiest solution for writing new tests cases.






  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • Connatix
  • BoatyardX
  • Telenav
  • .msg systems
  • Grab
  • Yardi
  • Colors in projects