news 2026/3/26 14:28:47

使用 UI Automator 改进 Espresso Android UI 测试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 UI Automator 改进 Espresso Android UI 测试

UI Automator: 丰富 Espresso Android UI 测试

添加几行代码来测试应用对设备旋转的反应

现在我们有了一个在不同屏幕上呈现不同布局的应用, 截图测试是验证布局的一个良好而有效的选择, 但我们如何在Espresso UI测试中测试应用在旋转设备时应用正确布局的能力呢?

我们需要UI Automator。👈

什么是 UI Automator?

UI Automator 是为 Android 应用创建自动化UI测试的工具. 它是 Android 测试支持库的一部分. 它使我们能够在被测应用和不同应用之间执行任务, 因此对于全面集成和端到端测试而言, 它非常有价值。

UI Automator 及其与 Espresso 的关系

虽然 UI Automator 为 Android 界面测试提供了出色的工具, 但人们经常讨论它与 Espresso 的关系. 正如官方文档所建议的, UI Automator 和 Espresso 有一些功能重叠, 但 Espresso 有更多的同步机制, 因此对于常见的 UI 测试, 它是首选。

范围

  • UI Automator允许我们对设备上的任何可见元素执行操作, 包括系统应用和硬件按钮. 因此, 它非常适合测试扩展到被测应用之外的用户流。

  • Espresso仅限于在被测应用内进行交互, 可对应用组件和状态进行更精细的控制。

使用案例

  • Espresso 擅长测试应用内的复杂交互, 如输入表单, 对话框和转换。

  • UI Automator 更适合我们必须与多个应用进行交互, 或在通知或不同网络状态等系统条件下检查应用行为的情况。

性能

  • 一般来说, Espresso 比 UI Automator 更快, 因为它直接与应用交互, 而UI Automator 可能会遇到一些问题。

  • UI Automator 可能会出现延迟, 因为它会扫描整个屏幕来执行操作。

结合使用 UI Automator 和 Espresso

将 UI Automator 和 Espresso 结合使用可发挥这两个框架的优势. 例如, Espresso可用于在我们的应用中进行详细测试, 而UI Automator则可以处理像权限对话框这样的情况, 这是Android操作系统的一部分, 还可以帮助改变屏幕方向。

这种组合为UI测试提供了一种全面的方法, 可确保应用特定功能的稳健性以及与系统级功能的集成。

设置UI自动化程序

假设我们的项目已为AndroidX测试正确设置. 要在项目中引入 UI Automator, 只需要一个新的依赖项。

  1. dependencies {

  2. ...

  3. androidTestImplementation('androidx.test.uiautomator:uiautomator:2.3.0')

  4. }

那么, 我们如何在测试中改变屏幕方向呢?

鉴于我们已经有了一些 Espresso UI 测试, 我们只需获取一个UIDevice的实例, 然后就可以开始了。​​​​​​​

  1. import androidx.test.uiautomator.UiDevice;

  2. import androidx.test.platform.app.InstrumentationRegistry;

  3. @RunWith(AndroidJUnit4::class)

  4. class MainActivityTest {

  5. @get:Rule

  6. val composeTestRule = createAndroidComposeRule<MainActivity>()

  7. val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())

  8. @After

  9. fun tearDown() {

  10. uiDevice.setOrientationNatural()

  11. }

  12. @Test

  13. fun appNavigationLayoutTest() {

  14. with(mainActivityTestRobot) {

  15. // Rotate to landscape

  16. uiDevice.setOrientationLeft()

  17. checkNavigationLayoutIsCorrect()

  18. // Rotate to portrait

  19. uiDevice.setOrientationNatural()

  20. checkNavigationLayoutIsCorrect()

  21. }

  22. }

  23. }

💡 InstrumentationRegistry.getInstrumentation()是来自androidx.test.espresso包。

我们如何运行与 WindowSizeClass 匹配的测试?

由于我们可以在不同的设备/模拟器上运行UI测试, 因此我们的生产代码应正确计算WindowSizeClass, 并相应地渲染预期布局. 如何确保使用正确的测试集来测试相应的屏幕尺寸/布局?

我们可以在测试中使用上文提到的InstrumentationRegistry来确定设备屏幕分辨率. 使用WindowSizeClass库中的WindowSizeClass.calculateFromSize(...)方法, 我们可以计算出当前的WindowSizeClass, 并有条件地针对该屏幕尺寸运行一组测试。​​​​​​​

  1. import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi

  2. import androidx.compose.material3.windowsizeclass.WindowSizeClass

  3. import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass

  4. @OptIn(ExperimentalMaterial3WindowSizeClassApi::class)

  5. fun getWindowSizeClass(): WindowSizeClass {

  6. val metrics = InstrumentationRegistry

  7. .getInstrumentation().targetContext.resources.displayMetrics

  8. val widthPx = metrics.widthPixels

  9. val heightPx = metrics.heightPixels

  10. val density = metrics.density

  11. val widthDp = widthPx / density

  12. val heightDp = heightPx / density

  13. return WindowSizeClass.calculateFromSize(

  14. size = DpSize(width = widthDp.dp, height = heightDp.dp)

  15. )

  16. }

  17. @Test

  18. fun checkNavigationLayoutIsCorrect() {

  19. // ...

  20. val windowWidthSizeClass = getWindowSizeClass().widthSizeClass

  21. if (windowWidthSizeClass == WindowWidthSizeClass.Compact) {

  22. assertNavigationBarIsDisplayed()

  23. } else {

  24. assertNavigationRailIsDisplayed()

  25. }

  26. // ...

  27. }

💡 我们在生产代码中使用的calculateWindowSizeClass()只能在可组合函数中运行, 因此我采用了这种变通方法。

触发下拉刷新的另一种方法

使用 Espresso, 我们可以使用.performTouchInput来触发一个swipeDown操作, 从而在我们的 Jetpack Compose UI 上模拟拉动刷新操作。

例如:​​​​​​​

  1. ...

  2. onNodeWithContentDescription(label = "pull_to_refresh")

  3. .performTouchInput {

  4. swipeDown(

  5. startY = 0f,

  6. endY = 500f,

  7. durationMillis = 1_000,

  8. )

  9. }

  10. ....

这要求我们在向下滑动之前首先找到一个现有的节点。

要使用 UI Automator 创建更逼真的下拉刷新测试场景, 我们可以直接在屏幕上模拟轻扫动作, 而无需先定位特定的UI元素. 这种方法可以模拟用户在屏幕上的任何位置自然地执行轻扫手势来启动拉动刷新, 而无需考虑具体的UI布局或元素位置。​​​​​​​

  1. val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())

  2. // Get the screen size

  3. val screenWidth = device.displayWidth

  4. val screenHeight = device.displayHeight

  5. // Calculate start and end positions for the swipe gesture

  6. val startX = screenWidth / 2 // Swipe from middle of the screen width

  7. val startY = screenHeight / 4 // Start swipe from 1/4th down the screen

  8. val endX = startX // End swipe at the same X-coordinate

  9. val endY = startY + (screenHeight / 2) // End swipe halfway down the screen

  10. // Perform the swipe gesture

  11. device.swipe(startX, startY, endX, endY, 50) // 50 steps to make the swipe smooth

💡 作为折衷, 轻扫动作需要更长的时间. 由于每一步的执行时间被控制在 5 毫秒, 因此在 100 步的情况下, 轻扫动作需要大约 1/2 秒才能完成。

使用 UI Automator 测试通知

如果你还在阅读, 那么让我们将 UI Automator 集成到 Espresso UI 测试中, 看看还有哪些可能的用例。

处理系统通知并与之交互是 UI Automator 的另一个亮点, 因为它可以在测试应用的上下文之外进行交互。​​​​​​​

  1. @Test

  2. fun checkNotifcationMessage() {

  3. val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())

  4. device.openNotification()

  5. // Assuming the notification has text content "New Message"

  6. val notification = device.findObject(UiSelector().textContains("New Message"))

  7. if (notification.exists() && notification.isEnabled) {

  8. notification.click()

  9. // Espresso tests to check app behaviour after clicking the notification

  10. }

  11. }

切换飞行模式--UI Automator 执行 Shell 命令的强大功能

在 UI 测试中模拟实际的 UI 操作来更改某些设备设置具有挑战性, 比如切换飞行模式。为此, UI Automator 提供了`executeShellCommand()方法。​​​​​​​

  1. @Test

  2. fun checkThingsShouldWorkInAirplaneMode() {

  3. val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())

  4. // Command to enable airplane mode

  5. device.executeShellCommand("settings put global airplane_mode_on 1")

  6. // Command to refresh the system's airplane mode state

  7. device.executeShellCommand("am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true")

  8. // Add a delay or wait for the state to change

  9. // Test code to verify app behavior with no network

  10. // Command to disable airplane mode

  11. device.executeShellCommand("settings put global airplane_mode_on 0")

  12. device.executeShellCommand("am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false")

  13. }

注意事项和权限:

  • 权限:确保我们的测试设置和环境允许执行这些操作. 修改全局设置需要WRITE_SECURE_SETTINGS权限, 非 root 设备上的普通应用无法获得该权限。

  • 已 root 设备和模拟器:在已 root 设备或模拟器上运行此类测试更为直接, 因为我们可以更好地控制设备设置。

  • 影响:请注意使用此类命令的广泛影响. 更改设备设置会影响测试中的应用, 其他应用和设备的正常运行。

💡 对于网络测试, 在可能的情况下, 在控制的测试环境中使用网络模拟可能仍是首选, 以保持一致性并便于设置。

总结

将 UI Automator 与 Espresso 集成是增强 Android UI 测试的绝佳方法. 它使我们能够在测试中模拟真实世界的场景, 如设备旋转和系统通知。

这种工具组合使我们能够超越应用的边界进行测试, 并确保我们的应用在各种条件下的表现符合预期。

只需添加几行代码, 我们就能进行更彻底, 更全面的测试, 确保我们的应用符合现代可用性标准, 并提供卓越的用户体验。

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/15 4:02:45

导出模型太大?cv_resnet18_ocr-detection ONNX精简教程

导出模型太大&#xff1f;cv_resnet18_ocr-detection ONNX精简教程 你是不是也遇到过这个问题&#xff1a;在 WebUI 的「ONNX 导出」页面点下按钮&#xff0c;等了半天&#xff0c;生成的 .onnx 文件动辄 120MB 甚至更大&#xff1f;部署到边缘设备卡顿、上传到云服务超时、推…

作者头像 李华
网站建设 2026/3/13 21:50:55

【柔性板通过重构实现减阻】基于经验阻力公式的柔性板简化模型,研究了引发重构的两大机制——面积缩减与流线化附Matlab代码

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 &#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室 &#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码及仿真…

作者头像 李华
网站建设 2026/3/13 17:26:40

对话式AI专家Kathleen McKeown荣获双重荣誉

Kathleen McKeown&#xff0c;某中心的学者&#xff0c;也是哥伦比亚大学的Henry and Gertrude Rothschild计算机科学教授&#xff0c;最近荣获了2023年电气与电子工程师学会&#xff08;IEEE&#xff09;社会基础设施创新奖&#xff0c;并当选为美国哲学学会&#xff08;APS&a…

作者头像 李华
网站建设 2026/3/18 16:32:12

Vue 中的 keep-alive 组件

Vue 中的 keep-alive 组件keep-alive 是 Vue 内置的一个抽象组件&#xff0c;用于缓存不活动的组件实例&#xff0c;而不是销毁它们。这可以保留组件状态或避免重新渲染&#xff0c;从而提升性能。 核心特性 组件状态保持&#xff1a;当组件在 <keep-alive> 中切换时&…

作者头像 李华
网站建设 2026/3/14 23:03:27

Qwen3-TTS开源

Qwen3-TTS&#xff08;通义千问3代文本转语音&#xff09;全家桶的开源&#xff0c;是阿里云在AI语音领域的重要布局&#xff0c;其意义不仅在于技术共享&#xff0c;更在于通过开放生态推动整个TTS&#xff08;Text-to-Speech&#xff0c;文本转语音&#xff09;技术的普及与创…

作者头像 李华