Skip to main content

Appium

This guide walks through Kolibrium's Appium module from first dependency to a working test. Each section builds on the previous one.

We'll be writing tests against a sample e-commerce Android app with package com.example.app.

1. Add the dependency

dependencies {
testImplementation("dev.kolibrium:kolibrium-appium:<version>")
}

tasks.test {
useJUnitPlatform()
}

2. Define your app

Model your application under test as a singleton extending AndroidApp, IosApp, or CrossPlatformApp. The app definition holds identifiers and launch configuration — analogous to Site in the Selenium module.

object MyAndroidApp : AndroidApp(
appPackage = "com.example.app",
appActivity = ".MainActivity",
)
object MyIosApp : IosApp(
bundleId = "com.example.ios",
)

For fresh installs (e.g. CI), point to the binary instead:

object MyAndroidApp : AndroidApp(
appPath = "/path/to/app.apk",
)

3. Define screens

Screens extend Screen<YourApp> and declare elements with locator delegates. The driver is resolved from Kolibrium's context automatically — no constructor parameters, no driver passing.

Locator delegates locate elements lazily: the element is found when you first access the property, not when you declare it. Available locator functions include accessibilityId, className, resourceId, and xpath. Each has a plural variant (e.g. accessibilityIds) that returns a list of elements.

class ProductsScreen : Screen<MyAndroidApp>() {
private val title by accessibilityId("title")

private val products by xpaths(
"""//*[@resource-id="${MyAndroidApp.appPackage}:id/productList"]/android.view.ViewGroup""",
)

fun titleText(): String = title.text

fun selectProduct(name: String) {
products.first { it.text == name }.click()
}
}

class ProductDetailsScreen : Screen<MyAndroidApp>() {
private val cartButton by resourceId("cartBt")

fun addToCart() {
cartButton.click()
}
}

4. Write a test

androidTest and iosTest are the test harnesses. Each manages the full lifecycle: creates a driver session, runs the test body, and guarantees cleanup.

Use on(::ScreenClass) { ... } to interact with a screen. Chain screens with .on(...):

@Test
fun checkout() = androidTest(app = MyAndroidApp) {
on(::ProductsScreen) {
titleText() shouldBe "Products"
selectProduct("Backpack")
}.on(::ProductDetailsScreen) {
addToCart()
}
}
@Test
fun checkout() = iosTest(app = MyIosApp) {
on(::ProductsScreen) {
titleText() shouldBe "Products"
}
}

Putting it all together

// App definition
object MyAndroidApp : AndroidApp(
appPackage = "com.example.app",
appActivity = ".MainActivity",
)

// Screens
class ProductsScreen : Screen<MyAndroidApp>() {
private val title by accessibilityId("title")

private val products by xpaths(
"""//*[@resource-id="${MyAndroidApp.appPackage}:id/productList"]/android.view.ViewGroup""",
)

fun titleText(): String = title.text

fun selectProduct(name: String) {
products.first { it.text == name }.click()
}
}

class ProductDetailsScreen : Screen<MyAndroidApp>() {
private val cartButton by resourceId("cartBt")

fun addToCart() {
cartButton.click()
}
}

// Test
class MyAppTest {
@Test
fun checkout() = androidTest(app = MyAndroidApp) {
on(::ProductsScreen) {
titleText() shouldBe "Products"
selectProduct("Backpack")
}.on(::ProductDetailsScreen) {
addToCart()
}
}
}