Your First Android Test

9 min read

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=ApiDemosTest

Watch 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 Inspector
  • SessionNotCreatedException — capabilities problem; check device name, APK path, Appium server logs
  • StaleElementReferenceException — element found but page changed; add an explicit wait before the interaction

// tip to track lessons you complete and pick up where you left off across devices.