Appium tests in Java need a project structure, a build tool, and a test framework. Maven handles dependencies and builds. TestNG handles test lifecycle, parallel execution, and reporting. This lesson walks through creating a project from scratch, adding the right dependencies, and wiring everything together so you can write your first test in the next lesson.
Prerequisites
Ensure you have JDK 11 or later installed:
java -version
javac -versionAnd Maven:
mvn -versionIf Maven is missing, install it via Homebrew on macOS (brew install maven) or download from maven.apache.org.
Create the Maven project
mvn archetype:generate \
-DgroupId=com.qa \
-DartifactId=appium-tests \
-DarchetypeArtifactId=maven-archetype-quickstart \
-DarchetypeVersion=1.4 \
-DinteractiveMode=falseThis creates the standard Maven directory layout:
appium-tests/
├── pom.xml
└── src/
├── main/java/com/qa/
└── test/java/com/qa/
All your test code goes in src/test/java/.
pom.xml dependencies
Replace the contents of pom.xml with:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qa</groupId>
<artifactId>appium-tests</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<appium.version>9.2.2</appium.version>
<testng.version>7.9.0</testng.version>
</properties>
<dependencies>
<!-- Appium Java Client -->
<dependency>
<groupId>io.appium</groupId>
<artifactId>java-client</artifactId>
<version>${appium.version}</version>
</dependency>
<!-- TestNG -->
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
</project>Key dependency: io.appium:java-client pulls in AndroidDriver, IOSDriver, AppiumBy, all capability options classes, and the Selenium dependency transitively. You do not need to add Selenium explicitly.
TestNG suite file
Create testng.xml in the project root:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Appium Tests" verbose="1">
<test name="Android Tests">
<classes>
<class name="com.qa.tests.LoginTest" />
</classes>
</test>
</suite>Base test class
Create src/test/java/com/qa/base/BaseTest.java:
package com.qa.base;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
public class BaseTest {
protected AndroidDriver driver;
@BeforeClass
public void setUp() throws MalformedURLException {
UiAutomator2Options options = new UiAutomator2Options()
.setDeviceName("emulator-5554")
.setPlatformVersion("14")
.setApp(System.getProperty("user.dir") + "/src/test/resources/app.apk")
.setNewCommandTimeout(Duration.ofSeconds(60));
driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), options);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}
@AfterClass
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
}Notes on this structure:
@BeforeClassruns once before all tests in the class — one driver per test class@AfterClassalways runs after all tests, even on failure — guarantees session cleanupimplicitlyWait(10)gives Appium 10 seconds to find elements before throwingNoSuchElementException- The APK path uses
System.getProperty("user.dir")to build an absolute path portably
First test class
Create src/test/java/com/qa/tests/LoginTest.java:
package com.qa.tests;
import com.qa.base.BaseTest;
import io.appium.java_client.AppiumBy;
import org.testng.Assert;
import org.testng.annotations.Test;
public class LoginTest extends BaseTest {
@Test
public void verifyLoginScreenLoads() {
String title = driver
.findElement(AppiumBy.accessibilityId("login_title"))
.getText();
Assert.assertEquals(title, "Sign In");
}
}App resource
Put the APK under src/test/resources/. For now, a demo APK works fine — the ApiDemos APK from the Appium GitHub repository is a good starting point:
mkdir -p src/test/resources
# Download ApiDemos-debug.apk from Appium's GitHub samplesRunning tests
mvn testMaven compiles your code, reads testng.xml, and runs the test classes listed there. The Surefire plugin generates reports in target/surefire-reports/.
To run a specific test class without updating testng.xml:
mvn test -Dtest=LoginTestProject structure at this point
appium-tests/
├── pom.xml
├── testng.xml
└── src/
└── test/
├── java/com/qa/
│ ├── base/BaseTest.java
│ └── tests/LoginTest.java
└── resources/
└── app.apk
This is the scaffold every subsequent chapter builds on.