UI Automator 测试框架提供了一组 API,用于构建与用户应用和系统应用交互的界面测试。
现代 UI Automator 测试简介
UI Automator 2.4 引入了一种简化的 Kotlin 友好型领域专用 语言 (DSL),可简化 Android 界面测试的编写。这个新的 API 表面侧重于基于谓词的元素查找和对应用 状态的显式控制。使用它可以创建更易于维护且更可靠的自动化测试。
借助 UI Automator,您可以从应用进程外部测试应用。这样,您就可以测试应用了缩减功能的发布版本。 在编写宏基准测试时,UI Automator 也 很有用。
现代方法的主要功能包括:
- 专用的
uiAutomator测试范围,可让测试代码更简洁、更具表现力。 - 用于查找具有明确谓词的界面元素的方法,例如
onElement、onElements和onElementOrNull。 - 用于条件元素的内置等待机制
onElement*(timeoutMs: Long = 10000) - 显式应用状态管理,例如
waitForStable和waitForAppToBeVisible。 - 与无障碍功能窗口节点直接交互,适用于多窗口测试 场景。
- 内置屏幕截图功能和
ResultsReporter,适用于视觉测试 和调试。
设置项目
如需开始使用现代 UI Automator API,请更新项目的
build.gradle.kts 文件以包含最新依赖项:
Kotlin
dependencies {
...
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0-alpha05")
}
Groovy
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.4.0-alpha05"
}
核心 API 概念
以下部分介绍了现代 UI Automator API 的核心概念。
uiAutomator 测试范围
在 uiAutomator { ... }
块中访问所有新的 UI Automator API。此函数会创建一个 UiAutomatorTestScope,为您的测试操作提供简洁
且类型安全的环境。
uiAutomator {
// All your UI Automator actions go here
startApp("com.example.targetapp")
onElement { textAsString() == "Hello, World!" }.click()
}
查找界面元素
使用带有谓词的 UI Automator API 来查找界面元素。借助这些谓词 ,您可以为属性(例如文本、选中或焦点 状态以及内容说明)定义条件。
onElement { predicate }:返回在默认超时时间内与 谓词匹配的第一个界面元素。如果该函数找不到匹配的元素,则会抛出异常。// Find a button with the text "Submit" and click it onElement { textAsString() == "Submit" }.click() // Find a UI element by its resource ID onElement { viewIdResourceName == "my_button_id" }.click() // Allow a permission request watchFor(PermissionDialog) { clickAllow() }onElementOrNull { predicate }:与onElement类似,但如果该函数在超时时间内找不到匹配的元素,则会返回null。它 不会抛出异常。将此方法用于可选元素。val optionalButton = onElementOrNull { textAsString() == "Skip" } optionalButton?.click() // Click only if the button existsonElements { predicate }:等待至少一个界面元素与给定的谓词匹配 ,然后返回所有匹配的界面元素的列表。// Get all items in a list Ui element val listItems = onElements { className == "android.widget.TextView" && isClickable } listItems.forEach { it.click() }
以下是使用 onElement 调用的几点提示:
链接
onElement调用以查找嵌套元素:您可以链接onElement调用,以按照父子 层次结构在其他元素中查找元素。// Find a parent Ui element with ID "first", then its child with ID "second", // then its grandchild with ID "third", and click it. onElement { viewIdResourceName == "first" } .onElement { viewIdResourceName == "second" } .onElement { viewIdResourceName == "third" } .click()通过传递表示 毫秒的值,为
onElement*函数指定超时时间。// Find a Ui element with a zero timeout (instant check) onElement(0) { viewIdResourceName == "something" }.click() // Find a Ui element with a custom timeout of 10 seconds onElement(10_000) { textAsString() == "Long loading text" }.click()
与界面元素交互
通过模拟点击或在可修改的 字段中设置文本,与界面元素交互。
// Click a Ui element
onElement { textAsString() == "Tap Me" }.click()
// Set text in an editable field
onElement { className == "android.widget.EditText" }.setText("My input text")
// Perform a long click
onElement { contentDescription == "Context Menu" }.longClick()
处理应用状态和监听器
管理应用的生命周期,并处理测试期间可能 出现的意外界面元素。
应用生命周期管理
这些 API 提供了控制受测应用状态的方法:
// Start a specific app by package name. Used for benchmarking and other
// self-instrumenting tests.
startApp("com.example.targetapp")
// Start a specific activity within the target app
startActivity(SomeActivity::class.java)
// Start an intent
startIntent(myIntent)
// Clear the app's data (resets it to a fresh state)
clearAppData("com.example.targetapp")
处理意外界面
借助 watchFor API,您可以为测试流程中可能出现的意外界面元素
(例如权限对话框)定义处理程序。这
会使用内部监听器机制,但提供了更高的灵活性。
import androidx.test.uiautomator.PermissionDialog
@Test
fun myTestWithPermissionHandling() = uiAutomator {
startActivity(MainActivity::class.java)
// Register a watcher to click "Allow" if a permission dialog appears
watchFor(PermissionDialog) { clickAllow() }
// Your test steps that might trigger a permission dialog
onElement { textAsString() == "Request Permissions" }.click()
// Example: You can register a different watcher later if needed
clearAppData("com.example.targetapp")
// Now deny permissions
startApp("com.example.targetapp")
watchFor(PermissionDialog) { clickDeny() }
onElement { textAsString() == "Request Permissions" }.click()
}
PermissionDialog 是 ScopedWatcher<T> 的一个示例,其中 T 是作为范围传递给 watchFor 中块的
对象。您可以根据此模式创建自定义
监听器。
等待应用可见性和稳定性
有时,测试需要等待元素变为可见或稳定。 UI Automator 提供了多个 API 来帮助实现此目的。
waitForAppToBeVisible("com.example.targetapp") 会等待具有给定软件包名称的界面元素
在可自定义的超时时间内出现在屏幕上。
// Wait for the app to be visible after launching it
startApp("com.example.targetapp")
waitForAppToBeVisible("com.example.targetapp")
使用 waitForStable() API 验证应用的界面在与用户交互之前是否被视为稳定
。
// Wait for the entire active window to become stable
activeWindow().waitForStable()
// Wait for a specific Ui element to become stable (e.g., after a loading animation)
onElement { viewIdResourceName == "my_loading_indicator" }.waitForStable()
将 UI Automator 用于宏基准测试和基准配置文件
将 UI Automator 用于使用 Jetpack Macrobenchmark 进行的性能测试,以及用于生成 基准配置文件,因为它提供了一种可靠的方式来 与您的应用交互,并从最终用户的角度衡量性能。
宏基准测试使用 UI Automator API 来驱动界面并衡量交互。
例如,在启动基准测试中,您可以使用 onElement 来检测界面
内容何时完全加载,从而衡量 完全显示所用时间
(TTFD)。在卡顿基准测试中,UI Automator API 用于滚动列表或
运行动画以衡量帧时序。startActivity() 或
startIntent() 等函数对于让应用进入正确状态非常有用,然后
开始衡量。
生成基准配置文件时,您可以自动执行应用的关键用户
历程 (CUJ),以记录哪些类和方法需要预编译。UI
Automator 是编写这些自动化脚本的理想工具。与其他方法相比,现代
DSL 基于谓词的元素查找和内置等待机制 (onElement)
可实现更可靠、更具确定性的测试执行。
这种稳定性可减少不稳定性,并确保生成的基准配置文件
准确反映在最重要的用户
流程期间执行的代码路径。
高级功能
以下功能对于更复杂的测试场景非常有用。
与多个窗口交互
借助 UI Automator API,您可以直接与界面 元素交互并检查界面元素。这对于涉及多个窗口的场景 (例如画中画 (PiP) 模式或分屏布局)尤其有用。
// Find the first window that is in Picture-in-Picture mode
val pipWindow = windows()
.first { it.isInPictureInPictureMode == true }
// Now you can interact with elements within that specific window
pipWindow.onElement { textAsString() == "Play" }.click()
屏幕截图和视觉断言
直接在测试中捕获整个屏幕、特定窗口或 单个界面元素的屏幕截图。这有助于进行视觉 回归测试和调试。
uiautomator {
// Take a screenshot of the entire active window
val fullScreenBitmap: Bitmap = activeWindow().takeScreenshot()
fullScreenBitmap.saveToFile(File("/sdcard/Download/full_screen.png"))
// Take a screenshot of a specific UI element (e.g., a button)
val buttonBitmap: Bitmap = onElement { viewIdResourceName == "my_button" }.takeScreenshot()
buttonBitmap.saveToFile(File("/sdcard/Download/my_button_screenshot.png"))
// Example: Take a screenshot of a PiP window
val pipWindowScreenshot = windows()
.first { it.isInPictureInPictureMode == true }
.takeScreenshot()
pipWindowScreenshot.saveToFile(File("/sdcard/Download/pip_screenshot.png"))
}
Bitmap 的 saveToFile 扩展函数可简化将捕获的
图片保存到指定路径的过程。
使用 ResultsReporter 进行调试
借助 ResultsReporter,您可以将测试工件(例如屏幕截图)
直接与 Android Studio 中的测试结果相关联,以便更轻松地进行检查和
调试。
uiAutomator {
startApp("com.example.targetapp")
val reporter = ResultsReporter("MyTestArtifacts") // Name for this set of results
val file = reporter.addNewFile(
filename = "my_screenshot",
title = "Accessible button image" // Title that appears in Android Studio test results
)
// Take a screenshot of an element and save it using the reporter
onElement { textAsString() == "Accessible button" }
.takeScreenshot()
.saveToFile(file)
// Report the artifacts to instrumentation, making them visible in Android Studio
reporter.reportToInstrumentation()
}
从旧版 UI Automator 迁移
如果您有使用旧版 API 表面编写的现有 UI Automator 测试,请使用 下表作为参考来迁移到现代方法:
| 操作类型 | 旧版 UI Automator 方法 | 新版 UI Automator 方法 |
|---|---|---|
| 入口点 | UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) |
将测试逻辑封装在 uiAutomator { ... } 范围内。 |
| 查找界面元素 | device.findObject(By.res("com.example.app:id/my_button")) |
onElement { viewIdResourceName == "my\_button" } |
| 查找界面元素 | device.findObject(By.text("Click Me")) |
onElement { textAsString() == "Click Me" } |
| 等待空闲界面 | device.waitForIdle() |
首选 onElement 的内置超时机制;否则,请使用 activeWindow().waitForStable() |
| 查找子元素 | 手动嵌套的 findObject 调用 |
onElement().onElement() 链接 |
| 处理权限对话框 | UiAutomator.registerWatcher() |
watchFor(PermissionDialog) |