UI Automator 是 Google 推出的用于 Android 应用程序自动化 UI 测试的框架,它在系统级别运行,并且可以与单个应用程序之外的用户界面元素进行交互。与 Espresso 不同,UI Automator 不依赖于特定应用程序的进程:它可以打开系统对话框、通知面板并在应用程序之间切换。根据 Google Android Developers,UI Automator 使用标准的 Accessibility Service 来访问设备的 UI 树。
要点
UI Automator 是一个用于 Android 功能性 UI 测试的框架,它在操作系统级别运行。它提供 API 来访问设备屏幕上的任何元素,无论它属于哪个应用程序 — 包括系统状态栏、权限对话框、主屏幕和第三方应用程序。这使得它对于测试超出单个应用程序范围的场景不可或缺。
在架构上,UI Automator 使用 Accessibility Service — 与 TalkBack、Switch Access 和其他无障碍工具使用的相同的服务。通过此服务,框架获取当前屏幕的完整 UI 组件树,并允许对它们执行操作:点击、滑动、文本输入、长按。
UI Automator 首次出现在 Android 4.3(API 18)中,此后一直是 Android Testing Support Library 的一部分,作为 Google 官方跨应用程序测试工具。在 AndroidX Test 中,它作为一个独立的工件 androidx.test.uiautomator:uiautomator 版本 2.3.0(2024)提供,支持从 API 18 开始的所有 Android 版本。
UI Automator 的工作原理 基于扫描当前屏幕的 Accessibility 树。当调用 findObject(selector) 方法时,框架遍历 View 层次结构,找到第一个满足 UiSelector 条件的元素,并返回一个 UiObject 对象 — 用于与真实 View 交互的代理。
一个典型的 UI Automator 测试从获取 UiDevice 实例开始,该实例代表物理设备。UiDevice 提供用于搜索元素、管理按钮按下(Home、Back、Recent)、旋转屏幕和截图的方法。通过 UiSelector 找到元素后,在 UiObject 上执行操作。
在下面的示例中,测试打开 Settings 应用程序,通过文本找到 “电池” 项目并点击它。UI Automator 不需要启动 Activity — 它可以与设备的任何屏幕一起使用,包括第三方应用程序。
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
// 打开设置屏幕
device.pressHome()
device.wait(Until.hasObject(UiSelector().text("设置")), 2000)
// 找到 “电池” 项目并点击
val batteryItem = device.findObject(
UiSelector().text("电池")
)
batteryItem.clickAndWait(Until.newWindow(), 3000)
UiDevice — 与设备交互的主要类。它提供用于搜索元素、模拟硬件按钮按下(Home、Back、Menu、Volume)、电源管理、截图和等待特定屏幕状态的方法。UiDevice 每个测试创建一次,并重复用于所有操作。
UiSelector — 是一个用于搜索 UI 元素的流畅 API。与 Espresso 中的 ViewMatchers 不同,UiSelector 不需要编译 — 搜索条件通过方法链形成:text()、className()、description()、resourceId()、index()。多个条件通过逻辑 AND 自动组合。
| UiSelector 方法 | 用途 |
|---|---|
| text(String) | 根据元素的精确文本搜索 |
| textContains(String) | 根据部分文本搜索 |
| resourceId(String) | 根据资源 ID 搜索(例如 com.example:id/button) |
| className(String) | 根据 View 的类名搜索 |
| description(String) | 根据 content-description 搜索 |
| childSelector(selector) | 在容器中搜索子元素 |
当屏幕上有多个具有相同文本的元素时,UiSelector 允许组合条件:根据 ID 找到容器,然后在其中 — 根据文本和类找到元素。这保证了所需组件的唯一标识。childSelector 方法将搜索区域缩小到指定的容器,从而加快 UI 树中的导航。
val scrollView = device.findObject(
UiSelector().resourceId("android:id/list")
)
// 在列表中找到带有 “Wi-Fi” 文本的元素
val wifiItem = scrollView.findObject(
UiSelector().text("Wi-Fi")
wifiItem.click()
跨应用程序(跨应用)测试 — 选择 UI Automator 的主要原因。该框架可以在应用程序之间切换,通过浏览器测试 OAuth 登录,检查系统对话框(权限、应用程序选择),并与系统状态栏、通知面板和锁定屏幕进行交互。
一个典型的跨应用测试场景:应用程序打开浏览器进行 OAuth 授权,用户输入用户名和密码,浏览器重定向回应用程序。UI Automator 在进程之间切换,在浏览器中找到输入字段,填写它们,然后点击 “登录”。
// 等待浏览器出现
device.wait(Until.hasObject(
UiSelector().packageName("com.android.chrome")
), 5000)
// 在浏览器中搜索电子邮件输入字段
val emailField = device.findObject(
UiSelector().className("android.widget.EditText").instance(0)
)
emailField.text = "user@example.com"
UI Automator 可以检查并关闭系统对话框 — 地理位置权限、通知、文件访问。这对于测试应用程序的首次启动至关重要,因为系统会顺序请求多个权限。没有 UI Automator,此类场景无法自动化,因为系统对话框不属于应用程序的进程。
选择 UI Automator 和 Espresso 之间的选择取决于测试场景。Espresso 针对单个应用程序的测试进行了优化,具有自动同步和最少的样板代码。UI Automator 适用于需要与系统、浏览器或多个应用程序交互的场景。
| 标准 | UI Automator | Espresso |
|---|---|---|
| 范围 | 整个设备,多个应用程序 | 单个应用程序 |
| 同步 | 手动(wait, sleep) | 自动(Idling Resource) |
| 速度 | 较慢(通过服务访问) | 较快(在进程内运行) |
| 系统 UI | 支持(Notifications, Quick Settings) | 不支持 |
| 搜索精度 | 基于属性的 UiSelector | 基于类型和层次结构的 ViewMatchers |
| 稳定性 | 较低(取决于时间) | 较高(自动等待) |
在实践中,这些框架通常一起使用:Espresso 以高稳定性覆盖主要应用程序的 UI 测试,而 UI Automator 则用于超出应用程序范围的场景 — OAuth 登录、系统权限、使用 Share Intent。这种组合以最小的测试维护成本提供了最大的 UI 覆盖。
连接 UI Automator 是通过在 build.gradle 中添加依赖项来完成的。该框架是 AndroidX Test 的一部分,不需要在清单中声明额外权限 — 对 Accessibility Service 的访问在仪器测试启动时自动配置。
最小配置包括 uiautomator 工件和标准的测试运行器 AndroidJUnitRunner。UI Automator 测试放置在 src/androidTest 目录中,并在具有 Android API 18+ 的模拟器或物理设备上运行。
dependencies {
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation("androidx.test:runner:1.6.1")
}
要获取 UiDevice 实例,请使用 InstrumentationRegistry.getInstrumentation()。UiDevice 应在 setUp() 方法中创建一次,并在类的所有测试中重复使用以节省设备资源。需要注意的是,UiDevice 不是线程安全的 — 所有操作必须在测试方法的单个线程中执行。在每个测试中创建新的 UiDevice 会导致开销和执行速度减慢。建议在 beforeClass 方法中创建一次 UiDevice,并为测试类的所有测试重复使用它。
与 Espresso 不同,UI Automator 没有自动同步。要等待元素出现,请使用 UiDevice.wait(condition, timeout) 方法配合 Until 对象:Until.findObject(selector)、Until.hasObject(selector)、Until.gone(selector)。没有正确的等待,测试会因竞态条件而不稳定 — 元素可能无法在搜索时出现在屏幕上。建议设置至少 3-5 秒的超时时间以确保稳定性。
常见问题
UI Automator 在 Accessibility Service 级别运行,可以与任何应用程序交互。Espresso 在单个应用程序的进程内运行,并使用与 UI 线程的自动同步。UI Automator 更适合跨应用场景,Espresso — 更适合单个应用程序的稳定测试。
是的,UI Automator 适用于所有具有 Android API 18+ 的设备。它不需要 root 访问权限 — 使用标准的 Accessibility Service,该服务在测试启动时通过 Instrumentation 激活。
UI Automator 使用 Accessibility Service 获取当前屏幕的完整 UI 组件树。然后 UiSelector 遍历此树,并根据给定的条件找到元素:文本、类、ID、content-description 或其组合。
是的,UiDevice.takeScreenshot(storePath) 方法可以截取当前屏幕的屏幕截图并保存到文件中。这对于调试很有用:当测试失败时,可以保存屏幕截图并分析屏幕状态。
UI Automator 没有自动同步,因此测试对时序敏感。如果动画尚未完成或 View 尚未渲染,findObject 可能找不到元素。解决方案 — 使用具有足够超时的 UiDevice.wait()。
总结
UI Automator 工具集涵盖了所有关键跨应用测试场景,是系统级 Android 自动化的标准。
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。