Run Appium Tests in Parallel on Multiple Devices Using TestNG

The use of mobile apps in every little thing of day-to-day life has led to a rapid pace of mobile app development. With such quick growth, it becomes crucial to deliver high-quality apps. The development teams need to ensure quick release time, which requires mobile app testing cycles to be fast and reliable. Testing application scenarios sequentially can take days or even weeks to complete. This is where parallel test execution improves the efficiency of the testing process. Parallel testing in Appium requires careful configuration to initialize multiple Appium server instances and manage unique device capabilities.. By doing so, you can maintain the vast test coverage by reducing the execution time for your tests. In this article, we will go through a step-by-step guide on how Appium parallel test execution can be done using Java. What is Parallel Testing in Appium? Parallel testing is the execution of multiple automated tests concurrently, instead of running them sequentially.Parallel testing in Appium involves instantiating multiple Appium server sessions and corresponding driver instances to execute test scripts concurrently on different devices or emulators. For instance, you need to test ten test cases on two devices. By using parallel testing, you can run these tests simultaneously, reducing the overall execution time by half. When used with TestNG, Appium allows you to run multiple test threads, each targeting a unique device or emulator configured with distinct Appium server ports and system ports. Why Choose Appium for Parallel Testing in Mobiles? -Cross-platform compatibility – The same test logic can be used to run tests for both Android and iOS. -Language flexibility – Appium supports multiple programming languages like Java, Python, JavaScript, and Ruby. -Real device and emulator support – You can easily integrate your tests with both local device grids and cloud-based devices. -Open source ecosystem – It is backed by a large developer community and is continuously updated. Benefits of Appium Parallel Testing -Faster Test Cycles – Parallel testing in Appium can greatly reduce test execution time by executing the tests in parallel rather than sequentially on different devices. -Increase in Test Coverage – In a limited time, you can execute multiple combinations of tests in varying test environments. -Early feedback – Defects can be caught in the early stages with increased test coverage. -Optimization of Resources – Multiple devices, emulators, or machines can be effectively utilized using the grid. How to Perform Appium Parallel Testing using TestNG TestNG is a popular open-source testing framework that can simplify parallel execution. We can use TestNG with Appium to configure parallel testing. There are two models through which TestNG parallel execution can be configured in the XML suite file- mobile-app-automation.jpg Parallel Test Methods: In this model, test methods within the same class run in separate threads when specified in the tag with parallel=”methods”. Parallel Classes: All the test methods of a class execute in the same thread and multiple test classes can run in parallel using separate threads. We will be using parallel test methods in our demonstration since we want all our tests to interact with separate Appium driver instances. Let us look at the scenario we will be automating. Step 1 – Launch Calculator application. Step 2 – In one test method, perform the multiplication of two numbers and validate the result. Step 3 – In another test method, perform the addition of two numbers and validate the result. Step 4- Close the instance. Let us look at what the code of the scenario would look like- package appium; import java.net.MalformedURLException; import java.net.URL; import org.openqa.selenium.remote.DesiredCapabilities; import org.testng.Assert; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import io.appium.java_client.AppiumBy; import io.appium.java_client.android.AndroidDriver; public class Calculator { AndroidDriver driver; @BeforeTest public void setUp() throws MalformedURLException { DesiredCapabilities dc = new DesiredCapabilities(); //Setting desired capabilities for the Android device with details like device name, version, etc dc.setCapability("platformName", "Android"); dc.setCapability("platformVersion","14"); dc.setCapability("deviceName", "Pixel6_TestGrid"); dc.setCapability("automationName", "UiAutomator2"); //Setting capability for application we want to test dc.setCapability("app", "/Users/macair/Downloads/calculator.apk"); //Instantiating Android Driver and using Appium server host and port driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), dc); } @Test public void multiply() { //Locating numbers, mathematical operator, and equals button on the calculator a

May 6, 2025 - 11:05
 0
Run Appium Tests in Parallel on Multiple Devices Using TestNG

The use of mobile apps in every little thing of day-to-day life has led to a rapid pace of mobile app development. With such quick growth, it becomes crucial to deliver high-quality apps. The development teams need to ensure quick release time, which requires mobile app testing cycles to be fast and reliable. Testing application scenarios sequentially can take days or even weeks to complete. This is where parallel test execution improves the efficiency of the testing process. Parallel testing in Appium requires careful configuration to initialize multiple Appium server instances and manage unique device capabilities.. By doing so, you can maintain the vast test coverage by reducing the execution time for your tests.

In this article, we will go through a step-by-step guide on how Appium parallel test execution can be done using Java.

What is Parallel Testing in Appium?

Parallel testing is the execution of multiple automated tests concurrently, instead of running them sequentially.Parallel testing in Appium involves instantiating multiple Appium server sessions and corresponding driver instances to execute test scripts concurrently on different devices or emulators.

For instance, you need to test ten test cases on two devices. By using parallel testing, you can run these tests simultaneously, reducing the overall execution time by half. When used with TestNG, Appium allows you to run multiple test threads, each targeting a unique device or emulator configured with distinct Appium server ports and system ports.

Why Choose Appium for Parallel Testing in Mobiles?

-Cross-platform compatibility – The same test logic can be used to run tests for both Android and iOS.
-Language flexibility – Appium supports multiple programming languages like Java, Python, JavaScript, and Ruby.
-Real device and emulator support – You can easily integrate your tests with both local device grids and cloud-based devices.
-Open source ecosystem – It is backed by a large developer community and is continuously updated.

Benefits of Appium Parallel Testing

-Faster Test Cycles – Parallel testing in Appium can greatly reduce test execution time by executing the tests in parallel rather than sequentially on different devices.
-Increase in Test Coverage – In a limited time, you can execute multiple combinations of tests in varying test environments.
-Early feedback – Defects can be caught in the early stages with increased test coverage.
-Optimization of Resources – Multiple devices, emulators, or machines can be effectively utilized using the grid.

How to Perform Appium Parallel Testing using TestNG

TestNG is a popular open-source testing framework that can simplify parallel execution. We can use TestNG with Appium to configure parallel testing. There are two models through which TestNG parallel execution can be configured in the XML suite file-
mobile-app-automation.jpg

  1. Parallel Test Methods: In this model, test methods within the same class run in separate threads when specified in the tag with parallel=”methods”.

  2. Parallel Classes: All the test methods of a class execute in the same thread and multiple test classes can run in parallel using separate threads.

We will be using parallel test methods in our demonstration since we want all our tests to interact with separate Appium driver instances.

Let us look at the scenario we will be automating.

Step 1 – Launch Calculator application.

Step 2 – In one test method, perform the multiplication of two numbers and validate the result.

Step 3 – In another test method, perform the addition of two numbers and validate the result.

Step 4- Close the instance.

Let us look at what the code of the scenario would look like-

package appium;

import java.net.MalformedURLException;
import java.net.URL;

import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.Assert;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import io.appium.java_client.AppiumBy;
import io.appium.java_client.android.AndroidDriver;

public class Calculator {

AndroidDriver driver;

@BeforeTest
public void setUp() throws MalformedURLException {

    DesiredCapabilities dc = new DesiredCapabilities();
    //Setting desired capabilities for the Android device with details like device name, version, etc
    dc.setCapability("platformName", "Android");
    dc.setCapability("platformVersion","14");
    dc.setCapability("deviceName", "Pixel6_TestGrid");
    dc.setCapability("automationName", "UiAutomator2");
    //Setting capability for application we want to test
    dc.setCapability("app", "/Users/macair/Downloads/calculator.apk");
    //Instantiating Android Driver and using Appium server host and port
     driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), dc);

}
@Test
public void multiply() {

    //Locating numbers, mathematical operator, and equals button on the calculator app
    driver.findElement(AppiumBy.id("com.google.android.calculator:id/digit_4")).click();
     driver.findElement(AppiumBy.id("com.google.android.calculator:id/op_mul")).click();
     driver.findElement(AppiumBy.id("com.google.android.calculator:id/digit_9")).click();
     driver.findElement(AppiumBy.id("com.google.android.calculator:id/eq")).click(); 
    //Storing the result in a string variable
    String result = driver.findElement(AppiumBy.id("com.google.android.calculator:id/result_final")).getText();
    System.out.println("The result is - "+result);
    //Asserting the result as expected
    Assert.assertEquals(result, "36");      
}

@Test
public void add() {

    //Locating numbers, mathematical operator, and equals button on the calculator app
    driver.findElement(AppiumBy.id("com.google.android.calculator:id/digit_3")).click();
     driver.findElement(AppiumBy.id("com.google.android.calculator:id/op_add")).click();
     driver.findElement(AppiumBy.id("com.google.android.calculator:id/digit_2")).click();
     driver.findElement(AppiumBy.id("com.google.android.calculator:id/eq")).click(); 
    //Storing the result in a string variable
    String result = driver.findElement(AppiumBy.id("com.google.android.calculator:id/result_final")).getText();
    System.out.println("The result is - "+result);
    //Asserting the result as expected
    Assert.assertEquals(result, "5");       
}


@AfterTest
public void tearDown() {
    //Closing the driver instance
    driver.quit();
}

}

If you execute the above code, it will run sequentially with one test running at a time. The results would look like below-
Perform Appium Parallel Testing

Now, assume that you want to run the same test on multiple devices. If you do that sequentially it will take a lot of time to execute. This is where you can make full use of parallel testing using Appium. Let us look at what changes would be required in this test to make it run parallelly using TestNG. We will create a configuration file in JSON format, which will consist of the desired capabilities required to run our tests on the different devices. We will be saving this file in our resources folder as shown in the screenshot below-
a configuration file in JSON format

We are using two devices from the TestGrid’s real device cloud for our test, and the device capabilities for them can be captured from the TestGrid device by clicking on the information icon.
TestGrid’s real device cloud

The deviceConfig.json file would look like below:

[
{
"deviceName": "Pixel 7",
"platformVersion": "14",
"platformName": "android",
"automationName": "UiAutomator2",
"udid": "Device UDID",
"tg:userToken": "Your token",
"systemPort": "4011",
"uiautomator2ServerLaunchTimeout": "90000",
"url": "http://remote-host:port/wd/hub"
},
{
"deviceName": "Galaxy A23 5G",
"platformVersion": "14",
"platformName": "android",
"automationName": "UiAutomator2",
"udid": "Device UDID",
"tg:userToken": "Your token",
"systemPort": "4012",
"uiautomator2ServerLaunchTimeout": "90000",
"url": "http://remote-host:port/wd/hub"
}
]

Next, we create a factory and thread-safe base class, which uses the ThreadLocal variable to ensure that each thread uses its own driver instance.

DriverManager.java
mobile-app-automation.jpg

package utils;
import io.appium.java_client.android.AndroidDriver;
// This class manages the AndroidDriver instance for each thread using ThreadLocal.
public class DriverManager {
// ThreadLocal ensures each thread running in parallel gets its own driver instance
private static ThreadLocal driver = new ThreadLocal<>();
// Returns the current thread's driver instance
public static AndroidDriver getDriver() {
return driver.get();
}
// Sets the driver instance for the current thread
public static void setDriver(AndroidDriver driverInstance) {
driver.set(driverInstance);
}
// Quits and removes the driver instance for the current thread
public static void quitDriver() {
if (driver.get() != null) {
driver.get().quit();
driver.remove();
}
}
}

Now we will create a BaseTest.java class that will read device config from JSON using Jackson and launch the AndroidDriver. It will handle the setup and teardown for each test dynamically.

package base;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.appium.java_client.android.AndroidDriver;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import utils.DriverManager;
import java.io.File;
import java.net.URL;
public class BaseTest {
// This method runs before each test method and sets up the driver for that test
@BeforeMethod(alwaysRun = true)
@org.testng({"deviceIndex"}) // Receive device index from XML
public void setUp(String deviceIndex) throws Exception {
int index = Integer.parseInt(deviceIndex); // Convert string to int
// Parse the JSON config file to read desired capabilities
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(new File("src/test/resources/deviceConfig.json")).get(index);
DesiredCapabilities capabilities = new DesiredCapabilities();
// Dynamically add capabilities from the JSON node
node.fieldNames().forEachRemaining(field -> {
if (!field.equals("url")) { // Skip the URL field (used separately)
capabilities.setCapability(field, node.get(field).asText());
}
});
// Set the Appium server URL from the JSON config
URL remoteUrl = new URL(node.get("url").asText());
// Initialize the driver and store it in ThreadLocal using DriverManager
AndroidDriver driver = new AndroidDriver(remoteUrl, capabilities);
DriverManager.setDriver(driver);
}
// This method runs after each test method and quits the driver
@AfterMethod(alwaysRun = true)
public void tearDown() {
DriverManager.quitDriver();
}
}

We will now write the test logic in CalculatorTest.java using DriverManager.getDriver() to interact with the app. Each test method in this test class will run on a separate device in parallel.

package tests;
import base.BaseTest;
import io.appium.java_client.AppiumBy;
import org.testng.Assert;
import org.testng.annotations.Test;
import utils.DriverManager;
// Inherits setup/teardown logic from BaseTest
public class CalculatorTest extends BaseTest {
// Test to perform multiplication (4 x 9)
@test
public void testMultiply() {
DriverManager.getDriver().findElement(AppiumBy.id("com.google.android.calculator:id/digit_4")).click();
DriverManager.getDriver().findElement(AppiumBy.id("com.google.android.calculator:id/op_mul")).click();
DriverManager.getDriver().findElement(AppiumBy.id("com.google.android.calculator:id/digit_9")).click();
DriverManager.getDriver().findElement(AppiumBy.id("com.google.android.calculator:id/eq")).click();
// Capture and print the result
String result = DriverManager.getDriver()
.findElement(AppiumBy.id("com.google.android.calculator:id/result_final")).getText();
System.out.println("Multiplication result: " + result);
// Validate the result
Assert.assertEquals(result, "36");
}
// Test to perform addition (3 + 2)
@test
public void testAdd() {
DriverManager.getDriver().findElement(AppiumBy.id("com.google.android.calculator:id/digit_3")).click();
DriverManager.getDriver().findElement(AppiumBy.id("com.google.android.calculator:id/op_add")).click();
DriverManager.getDriver().findElement(AppiumBy.id("com.google.android.calculator:id/digit_2")).click();
DriverManager.getDriver().findElement(AppiumBy.id("com.google.android.calculator:id/eq")).click();
// Capture and print the result
String result = DriverManager.getDriver()
.findElement(AppiumBy.id("com.google.android.calculator:id/result_final")).getText();
System.out.println("Addition result: " + result);
// Validate the result
Assert.assertEquals(result, "5");
}
}

Finally we will write the TestNG XML file and define multiple blocks, each targeting a different device index. Set parallel=”tests” and assign thread-count to match device count.

testng-parallel.xml

https://testng.org/testng-1.0.dtd">















Let us now execute the TestNG Suite using the XML and see the results of our appium parallel execution.
results of our appium parallel execution

And you can see the test methods are being executed one after the other on the devices parallelly.
→ If you want to learn more about testing iOS apps — including manual testing, automation frameworks, real device testing, and best practices — check out this guide on iOS app testing.

Best Practices for Appium Parallel Testing

1.Always use unique udid values for devices to avoid session conflicts.
2.For local execution, ensure each device has a dedicated Appium server instance, and that each uses a unique systemPort (and chromedriverPort if applicable).
3.Use ThreadLocal to avoid driver conflicts.
4.Always keep an eye on the performance metrics like CPU utilization, memory, etc. while running multiple instances.
5.Use cloud testing platforms like TestGrid for large-scale parallel testing.

Conclusion

Appium parallel testing is of great utility to teams that want faster release cycles without compromising on quality. By executing tests on multiple devices concurrently, you can drastically reduce execution time, and increase coverage along with quicker feedback cycles.

Using TestNG, ThreadLocal drivers, and configuration files, the scalability and maintainability of your automation scripts can be ensured. The parallel tests using Appium help teams work faster and efficient while soaring the productivity to achieve better results.

→ Originally published on TestGrid.io blogs: Appium Parallel Test Execution Guide.