You have Appium running, an emulator booted, and a Maven project configured. Now you write an actual test — one that installs an app, navigates a screen, interacts with elements, and asserts an outcome. This lesson builds a complete end-to-end test for a login flow on Android.
The app under test
Use the ApiDemos sample app, which is included in Appium's test resources and freely available. It contains dozens of interactive screens — buttons, lists, text inputs, dialogs — making it perfect for learning.
Download ApiDemos-debug.apk from the Appium repository and place it at src/test/resources/ApiDemos-debug.apk.
Updating BaseTest for Android
Update BaseTest.java to point to the ApiDemos APK:
UiAutomator2Options options = new UiAutomator2Options()
.setDeviceName("emulator-5554")
.setPlatformVersion("14")
.setApp(System.getProperty("user.dir") + "/src/test/resources/ApiDemos-debug.apk")
.setNewCommandTimeout(Duration.ofSeconds(60));Writing the test
Create src/test/java/com/qa/tests/ApiDemosTest.java:
package com.qa.tests;
import com.qa.base.BaseTest;
import io.appium.java_client.AppiumBy;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.Assert;
import org.testng.annotations.Test;
import java.time.Duration;
public class ApiDemosTest extends BaseTest {
@Test
public void verifyAppLaunchesAndShowsMenu() {
// Verify the main screen loaded
WebElement header = driver.findElement(
AppiumBy.androidUIAutomator("new UiSelector().text(\"API Demos\")")
);
Assert.assertTrue(header.isDisplayed(), "App header should be visible");
}
@Test
public void navigateToViewsSection() {
// Tap on the "Views" list item
driver.findElement(
AppiumBy.androidUIAutomator("new UiSelector().text(\"Views\")")
).click();
// Verify we navigated to the Views screen
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement viewsHeader = wait.until(
ExpectedConditions.visibilityOfElementLocated(
AppiumBy.androidUIAutomator("new UiSelector().text(\"Views\")")
)
);
Assert.assertTrue(viewsHeader.isDisplayed());
}
@Test
public void typeTextIntoInputField() {
// Navigate to Views → TextFields
driver.findElement(
AppiumBy.androidUIAutomator("new UiSelector().text(\"Views\")")
).click();
driver.findElement(
AppiumBy.androidUIAutomator("new UiSelector().text(\"Text\")")
).click();
driver.findElement(
AppiumBy.androidUIAutomator("new UiSelector().text(\"LogTextBox\")")
).click();
// Find the Add button and the text input
WebElement addButton = driver.findElement(
AppiumBy.accessibilityId("Add")
);
addButton.click();
// Verify something appeared in the log
WebElement logView = driver.findElement(
AppiumBy.id("io.appium.android.apis:id/text")
);
Assert.assertFalse(logView.getText().isEmpty(), "Log should contain text after clicking Add");
}
}What each locator strategy means here
AppiumBy.androidUIAutomator("new UiSelector().text(\"Views\")")
Finds the first element where the text attribute exactly equals "Views". UiSelector is Android-only but very readable.
AppiumBy.accessibilityId("Add")
Finds the element whose content-desc is "Add". Works cross-platform and is fast.
AppiumBy.id("io.appium.android.apis:id/text")
Finds by resource-id. Note the full package prefix — resource-id always includes the app package.
Explicit waits vs implicit waits
The BaseTest sets an implicit wait of 10 seconds. This tells Appium to poll for elements for up to 10 seconds before throwing NoSuchElementException. It is the simplest protection against timing issues.
For more specific conditions (waiting for text to appear, waiting for a button to become clickable), use explicit waits:
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
WebElement element = wait.until(
ExpectedConditions.elementToBeClickable(AppiumBy.accessibilityId("Submit"))
);
element.click();Do not mix large implicit waits with explicit waits — they interact in unexpected ways. A common pattern is to set implicit wait to 0 in BaseTest and use explicit waits consistently everywhere.
Reading text and checking visibility
// Get the text content of an element
String buttonText = driver.findElement(AppiumBy.accessibilityId("Login")).getText();
// Check if an element is displayed
boolean isVisible = driver.findElement(AppiumBy.id("com.example:id/error_msg")).isDisplayed();
// Check if an element is enabled
boolean isEnabled = driver.findElement(AppiumBy.id("com.example:id/submit")).isEnabled();Taking a screenshot on failure
Add screenshot capture to BaseTest to help debug failures:
import org.openqa.selenium.OutputType;
import org.testng.ITestResult;
import org.testng.annotations.AfterMethod;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@AfterMethod
public void screenshotOnFailure(ITestResult result) throws IOException {
if (!result.isSuccess()) {
File src = driver.getScreenshotAs(OutputType.FILE);
Path dest = Path.of("target/screenshots", result.getName() + ".png");
Files.createDirectories(dest.getParent());
Files.copy(src.toPath(), dest);
}
}Running the test
Start the Appium server and your emulator, then:
mvn test -Dtest=ApiDemosTestWatch the emulator — you should see the app install, launch, and be driven by your test code. The test should complete in about 20–30 seconds and report 3 tests passed.
Reading the output
Successful output:
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
If a test fails, look for:
NoSuchElementException— element not found; check locator with Appium InspectorSessionNotCreatedException— capabilities problem; check device name, APK path, Appium server logsStaleElementReferenceException— element found but page changed; add an explicit wait before the interaction