Parallel tests with Appium and Genymotion SaaS

2024-04-23

Important This tutorial requires Appium 2.0 or higher.

Launching automated tests can take a long time. This tutorial teaches how to parallelize tests to detect bugs as soon as possible and spend less time on test runs.

Requirements

Launch several devices in Genymotion SaaS

gmsaas recipes list lists recipes that can be started. UUID is the identifier used when starting an instance.

This step will create and launch several Genymotion devices:

# start a Samsung Galaxy S23 - 14.0 device
instance1=$(gmsaas instances start 37499e5d-6bee-46d1-b07a-e594ff3fcb0d device_14.0)

# start a Samsung Galaxy A14- 13.0 device
instance2=$(gmsaas instances start f90338c7-5e36-4e30-b376-f3252b08c23f device_13.0)

# start a Google Pixel 6 - 12.0 device
instance3=$(gmsaas instances start 53d71621-b0b8-4e5a-8cea-0055ea98988f device_12.0)

The instance UUID is printed on standard output once an instance is started.

Note: The following commands only work on Unix shells like Bash or Zsh. If you are on Windows, consider using bash.exe and call gmsaas.exe instead of gmsaas.

port1=10000 && port2=20000 && port3=30000
gmsaas instances adbconnect $instance1 --adb-serial-port=$port1
gmsaas instances adbconnect $instance2 --adb-serial-port=$port2
gmsaas instances adbconnect $instance3 --adb-serial-port=$port3

Keep the port numbers handy, as you will need them to configure the Appium server.

If you need a persistent adb port, you can add the parameter --adb-serial-port (optional).

Start appium server

Appium 1.7 and later allow parallel testing with a single Appium server. Before that, multiple Appium servers were required to test N devices in parallel.

Start an Appium server using the basic command:

appium

Write your tests in Python

We are using Python throughout this tutorial, but you may use your preferred language.

We have chosen Pytest as the test framework. It has several useful plugins like:

Here is a simple Python script:

# -*- coding: utf-8 -*-
import pytest
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.options.android import UiAutomator2Options

def create_android_driver(udid, systemPort):
    # Setting capabilities directly on the options object
    capabilities = {
        "appium:deviceName": "Genymotion Cloud PaaS",
        "platformName": "Android",
        "automationName": "UiAutomator2",
        "appium:udid": udid,
        "appium:systemPort": systemPort,
        "appium:appPackage": "com.android.settings",
        "appium:appActivity": ".Settings"
    }
    url = "http://localhost:4723"
    options = UiAutomator2Options()
    options.load_capabilities(capabilities)
    driver = webdriver.Remote(url, options=options)
    # Return the driver
    return driver

@pytest.mark.parametrize(
    "udid, systemPort",
    [
        ("localhost:10000", "8201"),
        ("localhost:20000", "8202"),
        ("localhost:30000", "8203"),
    ],
)
def test_sum(udid, systemPort):  # Accept udid and systemPort as parameters
    driver = create_android_driver(udid, systemPort)
    try:
        driver.find_element(by=AppiumBy.XPATH, value='//*[@text="Battery"]')
    finally:
        # Ensure proper teardown
        driver.quit()

How to run Python tests in parallel

Now, it’s time to run tests on all devices.

If we run the Python script as follows:

pytest test_example.py

Pytest will execute tests on one device at a time. This is not ideal for speed.

To run all tests in parallel, use the pytest-xdist plugin and execute:

pytest -n 3 test_example.py

-n indicates the number of worker processes. Here we start with 3 processes.

This enables running parallel tests on several Genymotion SaaS virtual devices using Appium and pytest.

Many thanks to Jonathan Lipps, one of the Appium maintainers and founder of Cloud Grey, for reviewing the article.

To learn more about Genymotion SaaS, see the SaaS user guide resources. To run parallelize tests using Java instead of Python, see Ellinor Kwok’s project on GitHub.