Skip to content

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.