TestKase Docs
AutomationTest Frameworks

Appium

Set up Appium for mobile testing and integrate test results with TestKase.

Overview

Appium is an open-source mobile automation framework for testing native, hybrid, and mobile web applications on iOS and Android. It follows a client-server architecture — the Appium server speaks the WebDriver protocol, and you write tests in your preferred language using one of the official clients (Java, JavaScript, or Python).

To integrate Appium results with TestKase, use a test runner that generates structured XML output. With TestNG, use --format testng and point the reporter at the TestNG results file. With JUnit, use --format junit.

Prerequisites

  • Node.js 18+ — required for the Appium server
  • Java 11+ or Python 3.8+ — for your test client
  • Android SDK (for Android testing) or Xcode (for iOS testing)
  • A physical device or emulator/simulator configured and accessible

Installation

Appium Server

Install the Appium server globally via npm:

npm install -g appium

Install the platform driver you need:

appium driver install uiautomator2   # Android
appium driver install xcuitest       # iOS

Java Client (Maven)

Add the Appium Java client to your pom.xml:

<dependency>
  <groupId>io.appium</groupId>
  <artifactId>java-client</artifactId>
  <version>9.1.0</version>
</dependency>

JavaScript Client

npm install --save-dev webdriverio @wdio/appium-service

Python Client

pip install Appium-Python-Client

Project Setup

The following example uses Java with TestNG. Add both the Appium client and TestNG to your pom.xml:

<!-- pom.xml -->
<dependencies>
  <dependency>
    <groupId>io.appium</groupId>
    <artifactId>java-client</artifactId>
    <version>9.1.0</version>
  </dependency>
  <dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>7.9.0</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>
    </plugin>
  </plugins>
</build>

Create a base test class that configures the desired capabilities for Android:

// src/test/java/com/example/BaseTest.java
package com.example;

import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.options.UiAutomator2Options;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;

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

public class BaseTest {
    protected AndroidDriver driver;

    @BeforeMethod
    public void setUp() throws MalformedURLException {
        UiAutomator2Options options = new UiAutomator2Options()
            .setPlatformName("Android")
            .setDeviceName("emulator-5554")
            .setApp("/path/to/app.apk")
            .setAutomationName("UiAutomator2");

        driver = new AndroidDriver(new URL("http://127.0.0.1:4723"), options);
    }

    @AfterMethod
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }
}

Writing Tests

Create a test class extending the base class:

// src/test/java/com/example/LoginTest.java
package com.example;

import io.appium.java_client.AppiumBy;
import org.openqa.selenium.WebElement;
import org.testng.Assert;
import org.testng.annotations.Test;

public class LoginTest extends BaseTest {

    @Test(description = "[48271] testValidLogin")
    public void testValidLogin() {
        WebElement emailField = driver.findElement(AppiumBy.accessibilityId("emailInput"));
        emailField.sendKeys("user@example.com");

        WebElement passwordField = driver.findElement(AppiumBy.accessibilityId("passwordInput"));
        passwordField.sendKeys("password123");

        WebElement loginButton = driver.findElement(AppiumBy.accessibilityId("loginButton"));
        loginButton.click();

        WebElement dashboard = driver.findElement(AppiumBy.accessibilityId("dashboardTitle"));
        Assert.assertTrue(dashboard.isDisplayed(), "Dashboard should be visible after login");
    }

    @Test(description = "[48272] testInvalidPassword")
    public void testInvalidPassword() {
        WebElement emailField = driver.findElement(AppiumBy.accessibilityId("emailInput"));
        emailField.sendKeys("user@example.com");

        WebElement passwordField = driver.findElement(AppiumBy.accessibilityId("passwordInput"));
        passwordField.sendKeys("wrong");

        WebElement loginButton = driver.findElement(AppiumBy.accessibilityId("loginButton"));
        loginButton.click();

        WebElement errorMsg = driver.findElement(AppiumBy.accessibilityId("errorMessage"));
        Assert.assertEquals(errorMsg.getText(), "Invalid credentials");
    }
}

Each test includes a 5-digit Automation ID in its @Test(description) annotation using square brackets. The @testkase/reporter CLI extracts these IDs using the regex \[(\d{5})\]. For the tests above:

  • 48271 → linked to the "valid login" test case in TestKase
  • 48272 → linked to the "invalid password" test case in TestKase

Generate Automation IDs in TestKase first, then embed them in your @Test(description) annotations. The [XXXXX] pattern can appear anywhere in the description string.

Running Tests

Start the Appium server in a separate terminal:

appium

Run the tests with Maven:

mvn test

TestNG generates an XML results file at test-output/testng-results.xml by default.

You can customize the TestNG output directory by configuring the maven-surefire-plugin or passing -DoutputDir=custom-output to your TestNG suite.

TestKase Integration

After tests complete and the TestNG XML is generated, report results to TestKase:

npx @testkase/reporter report \
  --token $TESTKASE_PAT \
  --project-id PRJ-1 \
  --org-id 1173 \
  --cycle-id TCYCLE-5 \
  --format testng \
  --results-file test-output/testng-results.xml

--cycle-id is optional. If not provided, results are reported to TCYCLE-1 — the master test cycle for the project.

Automation ID Mapping

The reporter extracts 5-digit Automation IDs from test descriptions using the [XXXXX] bracket pattern:

Test CodeExtracted ID
@Test(description = "[48271] testValidLogin")48271
@Test(description = "[48272] testInvalidPassword")48272
@Test(description = "[48273] testAddItem")48273

The [XXXXX] pattern can appear anywhere in the @Test(description) value. Generate the 5-digit ID in TestKase first, then embed it in your test annotation.

Complete Example

1. Test File

// src/test/java/com/example/LoginTest.java
package com.example;

import io.appium.java_client.AppiumBy;
import org.openqa.selenium.WebElement;
import org.testng.Assert;
import org.testng.annotations.Test;

public class LoginTest extends BaseTest {

    @Test(description = "[48271] testValidLogin")
    public void testValidLogin() {
        WebElement emailField = driver.findElement(AppiumBy.accessibilityId("emailInput"));
        emailField.sendKeys("user@example.com");

        WebElement passwordField = driver.findElement(AppiumBy.accessibilityId("passwordInput"));
        passwordField.sendKeys("password123");

        driver.findElement(AppiumBy.accessibilityId("loginButton")).click();

        WebElement dashboard = driver.findElement(AppiumBy.accessibilityId("dashboardTitle"));
        Assert.assertTrue(dashboard.isDisplayed());
    }

    @Test(description = "[48272] testInvalidPassword")
    public void testInvalidPassword() {
        driver.findElement(AppiumBy.accessibilityId("emailInput")).sendKeys("user@example.com");
        driver.findElement(AppiumBy.accessibilityId("passwordInput")).sendKeys("wrong");
        driver.findElement(AppiumBy.accessibilityId("loginButton")).click();

        WebElement errorMsg = driver.findElement(AppiumBy.accessibilityId("errorMessage"));
        Assert.assertEquals(errorMsg.getText(), "Invalid credentials");
    }
}

2. Start Appium and Run Tests

# Terminal 1 — start the Appium server
appium

# Terminal 2 — run the tests
mvn test

3. Report Results to TestKase

npx @testkase/reporter report \
  --token $TESTKASE_PAT \
  --project-id PRJ-1 \
  --org-id 1173 \
  --cycle-id TCYCLE-5 \
  --format testng \
  --results-file test-output/testng-results.xml

Troubleshooting

Appium server not starting

Verify that Node.js 18+ is installed (node -v) and that no other process is using port 4723. You can specify a different port with appium --port 4724. Also ensure the platform driver is installed:

appium driver list --installed

If the driver is missing, install it with appium driver install uiautomator2 (Android) or appium driver install xcuitest (iOS).

Device not found

For Android, confirm that the emulator is running or the device is connected with USB debugging enabled:

adb devices

For iOS, ensure the simulator is booted or the physical device is trusted. Check the deviceName and platformVersion in your desired capabilities match the available device.

Session creation failed

This usually means the desired capabilities are misconfigured. Common causes:

  • The app path does not point to a valid .apk or .ipa file
  • The platformVersion does not match the device/emulator OS version
  • The automationName is not set (should be UiAutomator2 for Android or XCUITest for iOS)

Run Appium with verbose logging to get detailed error output:

appium --log-level debug