UI tests¶
AutoMobile UI tests keep the test assertion in Kotlin or Swift and the device steps in a YAML plan. The same plan can be reviewed, reused, and run locally or in CI.
1. Install a test runner¶
Android
Android / Gradle¶
Add the JUnit runner to the module that owns the tests:
// app/build.gradle.kts
dependencies {
testImplementation("dev.jasonpearson.auto-mobile:auto-mobile-junit-runner:0.0.67")
}
The runner executes as a normal JVM test, so no test APK or
connectedAndroidTest task is required. Ensure adb is on PATH and an
Android device or emulator is available.
iOS
iOS / Swift Package Manager¶
Add AutoMobile from GitHub in Xcode (File → Add Package Dependencies…), then add the XCTestRunner product to the test target.
For a Swift package manifest, use the released package:
.package(url: "https://github.com/kaeawc/auto-mobile.git", from: "0.0.67")
from: resolves the newest compatible AutoMobile release; it is not an exact pin. The package requires Swift 6, macOS 14, and iOS 17.
2. Create a plan¶
Android
Put the plan in src/test/resources/test-plans/:
name: launch-app
description: Launch the app and verify the home screen
platform: android
steps:
- tool: launchApp
appId: com.example.app
clearAppData: true
- tool: observe
waitFor:
text: "Welcome"
timeout: 10000
- tool: terminateApp
appId: com.example.app
iOS
Put the plan in the iOS test bundle’s test-plans/ directory:
name: launch-app
description: Launch the app and verify the home screen
platform: ios
steps:
- tool: launchApp
appId: com.example.app
clearAppData: true
- tool: observe
waitFor:
text: "Welcome"
timeout: 10000
- tool: terminateApp
appId: com.example.app
3. Consume the plan¶
Android
Place this test under app/src/test/:
import dev.jasonpearson.automobile.junit.AutoMobilePlan
import dev.jasonpearson.automobile.junit.AutoMobileRunner
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.test.assertTrue
@RunWith(AutoMobileRunner::class)
class LaunchTest {
@Test
fun appLaunches() {
val result = AutoMobilePlan("test-plans/launch-app.yaml").execute()
assertTrue(result.success, result.output)
}
}
Run it with:
./gradlew :app:testDebugUnitTest --tests LaunchTest
iOS
Add the plan to the iOS test bundle and create an AutoMobileTestCase:
import XCTest
import XCTestRunner
final class LaunchTests: AutoMobileTestCase {
override var planPath: String {
"test-plans/launch-app.yaml"
}
override var cleanupOptions: AutoMobilePlanExecutor.CleanupOptions? {
.init(appId: "com.example.app", clearAppData: true)
}
func testAppLaunches() throws {
let result = try executePlan()
XCTAssertTrue(result.success, result.error ?? "AutoMobile plan failed")
}
}
Run the test target from Xcode or with xcodebuild test against a booted iOS
Simulator. Keep selectors semantic and add observe.waitFor steps at important
checkpoints so failures explain which state was missing.
4. Run a multi-device plan¶
Add device labels when a flow spans two users or devices. Steps for different labels run concurrently:
devices:
- label: sender
platform: ios
- label: recipient
platform: ios
steps:
- tool: launchApp
device: sender
appId: com.example.chat
- tool: launchApp
device: recipient
appId: com.example.chat
- tool: inputText
device: sender
text: Hello
Use barrier to make device tracks meet at a point, or criticalSection only
when they must serialize access to a shared resource.
5. Accessibility workflows¶
Use semantic labels and identifiers in plans, then verify the same elements in
observe output before interacting with them. To exercise a screen reader,
enable the default-off accessibility tool for the current MCP connection:
{
"name": "setToolEnabled",
"arguments": { "toolName": "accessibility", "enabled": true }
}
Call accessibility with talkback: true on Android or voiceover: true on
iOS, run the flow, and disable it afterward. Android also supports the
default-off debug tool accessibilityFocus for setting or clearing TalkBack
focus. Check text alternatives, content descriptions or accessibility labels,
focus order, contrast, and tap-target size as part of the assertions; support
for toggling services varies by device type and OS version.
6. Pin CI releases¶
Use one release version for the runner, daemon, and device helpers. Restart a shared daemon so it receives the pin, then check the environment before tests:
export AUTOMOBILE_VERSION=0.0.67
bunx @kaeawc/auto-mobile@0.0.67 --daemon restart
bunx @kaeawc/auto-mobile@0.0.67 --cli doctor
Replace 0.0.67 with the version used by your test runner dependency.