A practical mobile automation project using Appium, Java, Selenium WebDriver, TestNG, and Maven. This repository demonstrates Android mobile automation, parallel mobile execution, Appium server/session configuration, Android emulators, UiAutomator2 capabilities, Appium locators, scrolling, native app activation, TestNG lifecycle annotations, Maven dependency management, and mobile test framework design concepts.
Written by Brian McCarthy
- Project Overview
- Languages Used
- Technologies and Frameworks Used
- Methodologies Used
- File Structure
- Project File Links
- How the Framework Works
- Maven Dependency Summary
- TestNG Suite Summary
- Core Appium Functions and Concepts
- Code Methodology Summary
- Test-by-Test Explanation with Code Samples
- Additional Appium Java Code Examples
- Tutorial: Appium Mobile Automation in Java
- Guide: Building a Scalable Appium Java Framework
- Parallel Execution Guide
- Best Practices and Tips
- Troubleshooting
- Suggested Improvements
- Author
This project is a Java-based Appium mobile automation repository focused on Android automation and parallel execution. It includes examples that run multiple Android sessions in two different ways:
-
Separate Appium server instances for separate devices
Test1connects to Appium server port10000and uses emulatoremulator-5554.Test2connects to Appium server port10001and uses emulatoremulator-5556.
-
Single Appium server instance with different
systemPortvaluesTest3connects to one Appium server on port4723, uses emulatoremulator-5554, and setssystemPortto8231.Test4connects to one Appium server on port4723, uses emulatoremulator-5556, and setssystemPortto8230.
The project demonstrates how a mobile automation engineer can configure Android devices/emulators, start Appium sessions, activate native apps, locate mobile elements, automate scrolling, handle parallel sessions, and clean up drivers after execution.
| Language | Purpose |
|---|---|
| Java | Main automation language for Appium test classes, driver setup, TestNG lifecycle methods, and mobile actions |
| XML | Maven pom.xml dependency management and TestNG testng.xml suite configuration |
| Gherkin / Cucumber Concepts | Cucumber dependencies are included for behavior-driven test framework expansion |
| JSON | JSON libraries are included for future data handling, API payloads, or test data parsing |
| Shell / Command Line | Used to start Appium servers, emulators, Maven tests, and Android tooling |
| Technology / Framework | Purpose |
|---|---|
| Appium Java Client | Mobile automation client library for Android/iOS automation |
| Appium Server | Receives WebDriver commands and executes them on mobile devices/emulators |
| AndroidDriver | Appium driver used for Android app automation |
| UiAutomator2 | Android automation engine used by Appium for modern Android testing |
| Selenium Java | WebDriver foundation used by Appium Java Client |
| TestNG | Test runner, setup/teardown lifecycle, and parallel suite execution |
| Maven | Dependency management and test execution |
| Cucumber Java / Cucumber TestNG / Cucumber JUnit | BDD framework dependencies included for future feature-file based automation |
| Extent Reports Cucumber Adapter | Reporting dependency for Cucumber-based report generation |
| Log4j | Logging framework dependency |
| Apache POI | Excel/data-driven framework support dependency |
| JSON / json-simple | JSON test data or payload handling support |
| Android Emulator | Android virtual devices used as test execution targets |
| Appium Inspector | Recommended locator inspection tool for mobile apps |
The tests automate native Android apps by creating AndroidDriver sessions and interacting with Android UI elements through Appium locators.
Each test class uses TestNG annotations:
@BeforeTestfor driver setup@Testfor executable mobile test steps@AfterTestfor driver cleanup
The project demonstrates two parallel execution patterns:
- Multiple Appium servers with separate ports
- One Appium server with separate Android
systemPortvalues
Each session is configured through UiAutomator2Options, including platform name, automation engine, target emulator UDID, and optional system port.
The project uses Appium locator methods such as:
AppiumBy.id()AppiumBy.accessibilityId()AppiumBy.androidUIAutomator()
The tests use driver.activateApp() to bring a target native Android app package into the foreground.
The API Demos examples use UiScrollable and UiSelector to scroll vertically and horizontally to target mobile UI text.
The pom.xml includes dependencies for Cucumber, Extent Reports, Log4j, JSON handling, Apache POI, Selenium, TestNG, and Appium. This supports expansion into a larger data-driven or BDD mobile automation framework.
Appium-Mobile-Automation/
├── pom.xml # Maven project dependencies and Surefire configuration
├── testng.xml # TestNG suite running Test3 and Test4 in parallel
├── README.md # Project documentation
└── src/
└── test/
└── java/
└── demo1/
├── Test1.java # Parallel test using Appium server port 10000 and emulator-5554
├── Test2.java # Parallel test using Appium server port 10001 and emulator-5556
├── Test3.java # Single Appium server, emulator-5554, systemPort 8231
└── Test4.java # Single Appium server, emulator-5556, systemPort 8230
At a high level, the framework works like this:
- Maven loads dependencies from
pom.xml, including Appium Java Client, Selenium, TestNG, Cucumber, Extent Reports adapter, Log4j, JSON libraries, and Apache POI. - TestNG reads
testng.xmlto determine which classes to run and how to parallelize execution. - Each test class creates its own AndroidDriver in a
@BeforeTestsetup method. - UiAutomator2Options defines the Appium session capabilities, including Android platform, UiAutomator2 automation engine, emulator UDID, and system port when needed.
- The test method activates a target app with
driver.activateApp(). - Appium locators find mobile elements by accessibility ID, resource ID, or Android UiAutomator selector.
- The test performs mobile actions such as click, sendKeys, scroll, and tap.
- The teardown method quits the driver to close the Appium session and free the device/emulator.
The pom.xml identifies the project as:
<groupId>com.appiumguide.demo</groupId>
<artifactId>Appium-Guide</artifactId>
<version>0.0.1-SNAPSHOT</version>Important dependencies include:
| Dependency | Purpose |
|---|---|
io.appium:java-client:10.1.1 |
Appium Java client for mobile automation |
org.seleniumhq.selenium:selenium-java:4.43.0 |
Selenium WebDriver APIs used by Appium |
org.testng:testng:7.10.2 |
Test framework and parallel execution support |
io.cucumber:cucumber-java |
Cucumber step definition support |
io.cucumber:cucumber-testng |
Cucumber + TestNG integration |
io.cucumber:cucumber-junit |
Cucumber + JUnit support |
tech.grasshopper:extentreports-cucumber7-adapter |
Cucumber Extent Reports integration |
org.apache.logging.log4j:log4j-api/core |
Logging support |
org.apache.poi:poi and poi-ooxml |
Excel data-driven test support |
org.json:json and json-simple |
JSON data handling |
The Maven Surefire plugin is configured for class-level parallel execution with a thread count of 5.
The current testng.xml suite runs Test3 and Test4 in parallel by class:
<suite parallel="classes" name="Suite">
<test thread-count="5" parallel="classes" name="Test">
<classes>
<class name="demo1.Test3"/>
<class name="demo1.Test4"/>
</classes>
</test>
</suite>This means Test3 and Test4 can run at the same time. Because both use the same Appium server URL, each class must use a different Android systemPort value to prevent UiAutomator2 session conflicts.
| Function / Concept | Purpose |
|---|---|
UiAutomator2Options |
Defines Android Appium session capabilities |
setPlatformName("android") |
Specifies Android as the target platform |
setAutomationName("uiautomator2") |
Uses UiAutomator2 as the Android automation engine |
setUdid("emulator-5554") |
Targets a specific emulator/device |
setSystemPort(8231) |
Allocates a unique UiAutomator2 port for parallel sessions |
new AndroidDriver(new URL(...), cap) |
Starts a new Android Appium session |
driver.manage().timeouts().implicitlyWait() |
Applies implicit wait timing for element lookup |
driver.activateApp(packageName) |
Launches or brings an installed app to the foreground |
AppiumBy.id() |
Finds elements by Android resource ID |
AppiumBy.accessibilityId() |
Finds elements by accessibility ID/content description |
AppiumBy.androidUIAutomator() |
Finds elements using Android UiAutomator selector strings |
UiScrollable |
Scrolls vertically or horizontally to find off-screen elements |
sendKeys() |
Types text into mobile input fields |
click() |
Taps/clicks mobile elements |
driver.quit() |
Ends the Appium session |
The repository uses a direct Appium + TestNG style. Each test class owns its driver setup, test flow, and teardown. This makes the examples easy to understand and useful for learning how Appium sessions work.
The most important code methodologies are:
- Session setup per class: each test creates its own
AndroidDriver. - Device targeting by UDID: each test targets a specific emulator using
setUdid(). - Parallel safety: parallel tests use either separate Appium server ports or separate
systemPortvalues. - Native Android locators: tests use
AppiumBy.id,accessibilityId, andandroidUIAutomator. - App activation: tests launch installed apps through package names instead of installing APKs in the code.
- Scroll automation: tests use
UiScrollablefor vertical and horizontal scrolling. - TestNG lifecycle: setup, execution, and teardown are clearly separated.
File: src/test/java/demo1/Test1.java
Purpose: Automates the com.fastaguser Android app on emulator emulator-5554 by connecting to a dedicated Appium server running on port 10000.
@BeforeTest
public void stepup1() throws MalformedURLException {
UiAutomator2Options cap = new UiAutomator2Options();
cap.setPlatformName("android");
cap.setAutomationName("uiautomator2");
cap.setUdid("emulator-5554");
driver = new AndroidDriver(new URL("http://127.0.0.1:10000"), cap);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}
@Test
public void sessionidsample() throws InterruptedException {
Thread.sleep(5000);
driver.activateApp(app_package);
Thread.sleep(5000);
driver.findElement(AppiumBy.androidUIAutomator("new UiSelector().className(\"android.widget.ImageView\").instance(3)")).click();
driver.findElement(AppiumBy.androidUIAutomator("new UiSelector().className(\"android.widget.LinearLayout\").instance(6)")).click();
driver.findElement(AppiumBy.id("com.fastaguser:id/vehicle_id_txt")).sendKeys("1234");
driver.findElement(AppiumBy.id("com.fastaguser:id/btn_submit")).click();
}How it works: connects to port 10000, targets emulator-5554, activates com.fastaguser, clicks image/layout elements, enters vehicle ID 1234, and submits the form.
File: src/test/java/demo1/Test2.java
Purpose: Automates the Android API Demos app on emulator emulator-5556 by connecting to a separate Appium server running on port 10001.
@BeforeTest
public void setup2() throws MalformedURLException {
UiAutomator2Options cap = new UiAutomator2Options();
cap.setPlatformName("android");
cap.setAutomationName("uiautomator2");
cap.setUdid("emulator-5556");
driver = new AndroidDriver(new URL("http://127.0.0.1:10001"), cap);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}
@Test
public void test1() throws InterruptedException {
driver.activateApp("io.appium.android.apis");
driver.findElement(AppiumBy.accessibilityId("Views")).click();
driver.findElement(AppiumBy.androidUIAutomator("new UiScrollable(new UiSelector().scrollable(true))" +
".scrollIntoView(new UiSelector().text(\"Tabs\"))"));
driver.findElement(AppiumBy.accessibilityId("Tabs")).click();
driver.findElement(AppiumBy.accessibilityId("5. Scrollable")).click();
driver.findElement(AppiumBy.androidUIAutomator(
"new UiScrollable(new UiSelector().scrollable(true)).setAsHorizontalList().setMaxSearchSwipes(10)" +
".scrollIntoView(new UiSelector().text(\"TAB 30\"))"));
driver.findElement(AppiumBy.androidUIAutomator("new UiSelector().text(\"TAB 30\")")).click();
}How it works: opens API Demos, taps Views, scrolls vertically to Tabs, opens scrollable tabs, horizontally scrolls to TAB 30, and taps it.
File: src/test/java/demo1/Test3.java
Purpose: Automates the com.fastaguser Android app on emulator emulator-5554 through one shared Appium server at http://127.0.0.1:4723, using unique systemPort value 8231.
@BeforeTest
public void stepup1() throws MalformedURLException {
UiAutomator2Options cap = new UiAutomator2Options();
cap.setPlatformName("android");
cap.setAutomationName("uiautomator2");
cap.setUdid("emulator-5554");
cap.setSystemPort(8231);
driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), cap);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}How it works: uses one Appium server and a unique UiAutomator2 systemPort to avoid conflicts during parallel Android execution.
File: src/test/java/demo1/Test4.java
Purpose: Automates the API Demos app on emulator emulator-5556 through the same Appium server at http://127.0.0.1:4723, using systemPort value 8230.
@BeforeTest
public void setup2() throws MalformedURLException {
UiAutomator2Options cap = new UiAutomator2Options();
cap.setPlatformName("android");
cap.setAutomationName("uiautomator2");
cap.setUdid("emulator-5556");
cap.setSystemPort(8230);
driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), cap);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}How it works: uses the same Appium server as Test3, but targets a different emulator and uses a different systemPort, making parallel execution stable.
UiAutomator2Options options = new UiAutomator2Options();
options.setPlatformName("android");
options.setAutomationName("uiautomator2");
options.setUdid("emulator-5554");
AndroidDriver driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), options);driver.activateApp("io.appium.android.apis");
driver.findElement(AppiumBy.accessibilityId("Views")).click();
driver.findElement(AppiumBy.id("com.fastaguser:id/vehicle_id_txt")).sendKeys("1234");driver.findElement(AppiumBy.androidUIAutomator(
"new UiScrollable(new UiSelector().scrollable(true))" +
".scrollIntoView(new UiSelector().text(\"Tabs\"))"
));- Install Java JDK and Maven.
- Install Node.js and npm.
- Install Appium:
npm install -g appium. - Install UiAutomator2 driver:
appium driver install uiautomator2. - Install Android Studio and configure Android SDK variables.
- Start Android emulators and verify them with
adb devices. - Start Appium with
appium -p 4723or run separate servers on10000and10001. - Run tests with
mvn testormvn test -DsuiteXmlFile=testng.xml.
Recommended future framework layers:
src/main/java/framework/
├── base/BaseTest.java
├── config/ConfigReader.java
├── drivers/DriverFactory.java
├── pages/ApiDemosHomePage.java
├── pages/FastagHomePage.java
├── utils/WaitUtils.java
├── utils/ScreenshotUtils.java
└── reporting/ExtentReportManager.java
A reusable driver factory can centralize Appium session creation:
public AndroidDriver createDriver(String udid, int systemPort) throws MalformedURLException {
UiAutomator2Options options = new UiAutomator2Options();
options.setPlatformName("android");
options.setAutomationName("uiautomator2");
options.setUdid(udid);
options.setSystemPort(systemPort);
return new AndroidDriver(new URL("http://127.0.0.1:4723"), options);
}appium -p 10000
appium -p 10001Used by Test1.java and Test2.java.
appium -p 4723Used by Test3.java and Test4.java.
Every parallel Android UiAutomator2 session needs a unique systemPort.
- Prefer
AppiumBy.accessibilityId()when available. - Use
AppiumBy.id()for stable Android resource IDs. - Avoid brittle
className().instance()locators when better IDs exist. - Replace
Thread.sleep()with explicit waits. - Always call
driver.quit()in teardown. - Keep device UDIDs and ports in config files or TestNG parameters.
- Use unique
systemPortvalues for parallel Android sessions. - Use Appium Inspector to verify locators.
- Capture screenshots on failures.
- Move repeated workflows into page objects.
Start Appium:
appium -p 4723adb devicesConfirm the UDID in code matches the connected emulator.
Give each Android session a unique systemPort.
adb shell pm list packages | grep fastag
adb install path/to/app.apkCheck whether the element is off-screen, inside a different app screen, or has a changed locator.
- Add reusable base classes and page objects.
- Add explicit waits instead of
Thread.sleep(). - Add screenshot-on-failure support.
- Add Log4j configuration and Extent Reports output examples.
- Add
.apksetup instructions for tested apps. - Add Cucumber feature files if BDD is intended.
- Add Jenkins or GitHub Actions CI instructions.
- Add config files for Appium server URLs, UDIDs, app packages, and ports.
- Add
.gitignorefor reports, screenshots, logs, APKs, and local device files. - Add separate TestNG suites for separate-server and single-server parallel strategies.
Written by Brian McCarthy
This repository demonstrates Appium mobile automation in Java with AndroidDriver, UiAutomator2Options, Appium locators, TestNG lifecycle methods, Maven dependency management, native app activation, mobile scrolling, and parallel Android execution strategies.