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()
}
}
}