恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Android UI自动化测试:UI Automator与Espresso混合实战指南

  • 首页
  • 资讯中心
  • /
  • Android UI自动化测试:UI Automator与Espresso混合实战指南

相关资讯

Android Activity 功能代码实战:生命周期、状态保存与避坑指南 2026/10/10 10:50:38
Java连接PostgreSQL完整指南:JDBC驱动、连接池与避坑清单 2026/10/10 10:50:38
激活 4B 的百亿模型:A4B 到底省了什么、又骗了什么 2026/10/10 10:45:38

最新资讯

UI自动化跳过登录的工程化实践:五种方案与避坑指南
php数组合并的二种方法
信创测试中的异常场景设计与故障注入实战
50.8k 星怎么来的:一个纯 Markdown 文件比插件传播快,三个结构性原因
Python爬虫到DCGAN人脸生成:数据清洗决定模型上限的完整链路
港科大工学院MSc体验日全记录:课程选择与申请关键点解析

今日推荐

Codex 总用英文回答?从 AGENTS.md 到 config.toml 的中文输出调优指南
OpenClaw 自定义插件开发完整指南(2026最新版):从 TypeScript 到 npm 发布
基于Spark的电影推荐系统全链路实战:从爬虫到Web展示

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Android UI自动化测试:UI Automator与Espresso混合实战指南

发布时间:2026/10/10 10:50:38
Android UI自动化测试:UI Automator与Espresso混合实战指南 1. 为什么用 UI Automator 补 Espresso 的短板1.1 Espresso 很强但守得住“自己家”却出不了“门”做了几年 Android UI 自动化测试最常用的工具就是 Espresso。它的设计思路很对我胃口所有操作都会自动等待主线程进入空闲状态然后再去查找控件、执行点击和断言所以写出来的用例非常稳定基本不会出现那种“因为动画没结束就找不到 View”的随机失败。对于应用内部的页面流转、表单填写、列表加载、组件验证这些场景Espresso 几乎是首选代码写起来也很顺手一行onView(withId(R.id.btn_login)).perform(click())就能搞定。但 Espresso 有个天生的边界——它只能访问当前被测应用进程内的 View。什么意思呢一旦测试场景涉及到系统级的对话框、通知栏、其他 App 的页面Espresso 就完全抓瞎了。我早期做自动化时最痛苦的就是处理运行时权限弹窗。虽然可以用GrantPermissionRule在测试启动时直接授予权限可那只是解决了测试前置条件并不是真的在测用户点击“允许”这个交互路径。更别说有些国产 ROM 还会在系统权限弹窗之外再插一层自己的授权框Espresso 根本看不到那个窗口。类似的场景还有跳转到系统设置页去修改某个开关、点击分享菜单后进入微信或钉钉、从通知栏进去某个深链页面。这些全是 UI 测试里非常常见的真实需求但只靠 Espresso 一条腿很难走通。1.2 UI Automator 的定位刚好相反UI Automator 是 Android 提供的另一套 UI 测试框架它基于 Accessibility 服务去读取整个屏幕上的控件树所以它看的不是“被测应用的 View 层级”而是“系统全局的窗口层级”。换句话说只要是屏幕上显示出来的东西不管它属于你的 App、属于系统界面还是属于另一个第三方应用UI Automator 都有机会找到并操作它。这才是它能和 Espresso 形成互补的根本原因。UI Automator 最常用的类就是UiDevice它相当于一个“遥控器”可以模拟按键、打开通知栏、操作屏幕坐标。还有一个UiObject2/UiObject的概念用来查找和操作控件。新版androidx.test.uiautomator里的BySelector写起来也挺顺手比如device.findObject(By.text(允许))、device.findObject(By.res(com.android.permissioncontroller:id/permission_allow_button))。这些查找不局限于某个应用只要当前屏幕上有符合条件的节点基本都能拿到。当然它也有自己的短板。UI Automator 没有 Espresso 那种“自动等待主线程空闲”的同步机制你找控件的时候如果界面还在加载就得自己加等待逻辑。而且它是黑盒定位拿不到 View 的内部状态不能像 Espresso 那样做很深的状态断言。所以我的结论很简单这两个工具不是替代关系而是配合关系——用 Espresso 保证应用内交互的稳定用 UI Automator 处理跨应用和系统级交互。1.3 真正需要“Espresso UI Automator”搞定的场景我梳理了一下凡是符合下面这几类特征的测试用例基本都得把 UI Automator 拉进来系统级权限弹窗比如定位权限、存储权限、相机权限的授予流程这类交互本身就是被测业务的一部分不该被规则跳过。通知栏交互发送通知后拉下通知栏验证内容点击通知跳转到某页面或者测试快捷开关。跨应用协作分享到微信/QQ/钉钉、拉起第三方登录、跳转地图导航、打开文件管理器选文件。系统设置跳转从应用内跳转到系统设置页修改某些开关后再返回。绕过无法直接操作的遮挡层某些 ROM 会在应用页面顶部叠加自己的安全提示浮层Espresso 会因此点击失败这时用 UI Automator 把这个浮层的关闭按钮点掉再继续 Espresso 的操作。把这些场景拉出来之后问题就不是“要不要用 UI Automator”而是“怎么在同一个测试类里让两个框架协作得足够顺滑”。下面我会从依赖接入、典型场景代码、常见问题三个维度把我在项目里验证过的做法分享出来。2. 环境准备依赖接入与前置配置2.1 Gradle 依赖这样加最稳先说配置。Android 官方的测试依赖里Espresso 和 UI Automator 都在androidx.test组下加依赖不是什么难事难的是版本搭配和冲突处理。我给项目里用的是这么一套androidTestImplementation androidx.test:runner:1.5.2 androidTestImplementation androidx.test:rules:1.5.0 androidTestImplementation androidx.test.ext:junit:1.1.5 androidTestImplementation androidx.test.espresso:espresso-core:3.5.1 androidTestImplementation androidx.test.espresso:espresso-contrib:3.5.1 androidTestImplementation androidx.test.uiautomator:uiautomator:2.2.0注意一点espresso-contrib这个库会带进来一堆 RecyclerView、Drawer 相关支持类它内部依赖的androidx.test版本可能和其他库不一样。我遇到过因为espresso-contrib传递依赖导致InstrumentationRegistry版本冲突的情况。处理方式是在依赖里强制指定版本或者把 transitive 排除掉再手动引入需要的模块。如果项目里同时用了老牌的com.android.support.test:uiautomator建议统一迁到androidx.test.uiautomator不然两个版本混在一起会有方法冲突运行时容易报NoSuchMethodError。2.2 测试运行器与 Instrumentation 配置紧接着是defaultConfig里的测试运行器配置android { defaultConfig { testInstrumentationRunner androidx.test.runner.AndroidJUnitRunner } }这个配置大部分项目都有没什么好说的。真正需要留意的是测试方法里的上下文获取方式。老的写法是getInstrumentation()但推荐用InstrumentationRegistry.getInstrumentation()val instrumentation InstrumentationRegistry.getInstrumentation() val device UiDevice.getInstance(instrumentation)UiDevice.getInstance()不是每次调用都新建一个实例它内部是单例你随时都可以拿。拿到UiDevice之后调用device.waitForIdle()或者在查找时用Until.findObject就能控制等待节奏。2.3 先搞清楚几个容易踩的前置坑依赖和配置层面我提前踩过几个坑值得专门提一下。第一个是 UI Automator 对 Android 版本有要求。官方文档写的是 API 18 及以上现在项目最低版本基本都在 API 21 以上这不是大问题。但如果测试跑在非常老的低版本模拟器上UiDevice.openNotification()这类方法会直接返回 false搜索控件也会异常。建议测试机的 API 版本至少 24 以上不仅因为稳定性更好也因为新版通知栏结构和权限弹窗界面更统一。第二个是测试方法所在的包名和被测应用是否一致。UI Automator 的搜索是全局的不关心包名但如果你用RunWith(AndroidJUnit4::class)跑测试要保证testInstrumentationRunnerArguments里没有奇怪的过滤条件否则可能出现“单测能跑、多测一起跑就串台”的诡异问题。出现这种问题的时候先查 CI 配置里有没有设置clearPackageData或targetPackage。第三个坑是如果项目开了混淆被测应用的资源 ID 可能会被改掉导致 UI Automator 用By.res(包名:id/xxx)找不到控件。Espresso 其实也有这个问题但因为测试代码和被测应用一起编译R 类里的 ID 是同一个Espresso 通常不受影响。UI Automator 就惨了直接传字符串资源 ID混淆之后 ID 没变变量名可能变所以一般问题不大。真正要命的是第三方应用或系统界面里的资源 ID 在不同 ROM 上不一样这个后面会专门讲。3. 核心实战四类场景把 UI Automator 玩起来3.1 权限弹窗用 UI Automator 接管系统级对话框权限弹窗是“Espresso 失灵、UI Automator 顺手”的第一典型场景。原生 Android 8 以上的权限弹窗由permissioncontroller处理在 9.0 以下包名可能是com.android.packageinstaller不同版本、不同厂商的包名都有差异。所以我不建议完全依赖某个固定的资源 ID更稳的做法是优先按文本匹配再按资源 ID 兜底。这是一个我在项目里常用的权限弹窗处理函数fun clickSystemDialogButton(buttonText: String): Boolean { val device UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) // 先等弹窗出现超时时间给足 val target device.wait( Until.findObject(By.text(buttonText)), 10_000 ) if (target ! null) { target.click() return true } // 兜底按资源 ID 找适配不同 Android 版本的权限按钮 val resIds arrayOf( com.android.permissioncontroller:id/permission_allow_button, com.android.packageinstaller:id/permission_allow_button ) for (resId in resIds) { val byRes device.wait(Until.findObject(By.res(resId)), 2_000) if (byRes ! null) { byRes.click() return true } } return false }这里有两个细节值得注意。第一wait的返回值是UiObject2?如果超时没找到就返回 null所以可以直接判空。第二点击之前最好再确认一下这个按钮是否可点击有的 ROM 会在弹窗进场动画期间先让按钮处于 disabled 状态直接调click()没有效果但不报错后面用例就挂在一半。稳妥做法是加上isEnabled判断if (target.isEnabled) { target.click() } else { Thread.sleep(500) target.click() }有人会觉得Thread.sleep很不优雅但在 UI Automator 的场景里它其实是成本最低的稳定性兜底。我一般会在重试逻辑里用wait先等等不到再用短睡不会让整个测试被一个sleep(10_000)卡死。3.2 通知栏与快捷设置验证系统交互通知栏属于系统 UIEspresso 碰不到UI Automator 可以直接拉下来。核心方法是device.openNotification()和device.openQuickSettings()。一个典型的通知验证场景是App 里发送一条通知然后用UiDevice拉下通知栏验证通知标题和内容文本。代码大概长这样Test fun testNotificationContent() { // 业务操作触发通知这部分用 Espresso 完成 onView(withId(R.id.btn_send_notification)).perform(click()) val device UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) assertTrue(通知栏打开失败, device.openNotification()) // 通知栏打开后等待一下动画和列表加载都需要时间 val title device.wait( Until.findObject(By.text(你的订单已发货)), 5_000 ) assertNotNull(通知标题未找到, title) }这里我要多说一句openNotification()只是把通知栏拉下来但它不保证通知已经完全渲染。有的模拟器第一次打开通知栏非常慢后续就正常所以在查找通知内容时wait是非常必要的。还有一个体验上的坑如果测试机上锁屏了openNotification()经常失效所以测试前的 setup 里最好先确保屏幕是亮着的。可以用device.wakeUp()或者检查device.isScreenOn。不过 wakUp 之后如果锁屏界面还在通知栏照样拉不下来这就需要在测试环境层面保证是解锁状态。对 CI 里的虚拟机来说一般默认就是解锁的真机上偶尔会遇到这个问题。如果想测试快捷开关比如验证“点击通知栏里的某个快捷开关后系统设置发生变化”可以用device.openQuickSettings()然后通过文本或资源 ID 找到开关device.openQuickSettings() val wifiSwitch device.wait( Until.findObject(By.textContains(WLAN)), 5_000 ) wifiSwitch?.click()不同 ROM 对快捷开关的文本和图标差异比较大真机上如果没找到优先用uiautomator dump看当前界面树再决定用By.desc还是By.text。这部分我会在“常见问题”里细讲。3.3 跨应用分享从分屏到返回的完整链路跨应用分享是另一个“非 UI Automator 不可”的场景。Espresso 只能点击 App 内的分享按钮但点完之后弹出的系统分享面板是另一个窗口里面列的各种目标应用也不是被测应用的 View这时候就得靠 UI Automator 接力。我做过一个比较典型的用例在应用里点击“分享到微信”。完整链路是Espresso 点击分享按钮系统弹出分享面板。UI Automator 在分享面板里找微信的入口并点击。微信打开分享确认页面。UI Automator 在微信页面上点击“发送”。点击返回回到被测应用Espresso 继续断言。代码片段如下Test fun testShareToWeChat() { // 第一步Espresso 触发分享 onView(withId(R.id.btn_share)).perform(click()) val device UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) // 第二步在系统分享面板中找微信 val wechat device.wait( Until.findObject(By.textContains(微信)), 10_000 ) assertNotNull(wechat) wechat.click() // 第三步等微信分享页出现点“发送”按钮 val send device.wait( Until.findObject(By.text(发送)), 10_000 ) assertNotNull(send) send.click() // 第四步返回到被测应用 device.pressBack() device.waitForIdle() // 回到 Espresso断言分享结果回调或页面状态 onView(withId(R.id.tv_share_result)).check(matches(withText(分享成功))) }这段代码看起来简单实际跑的时候最容易翻车的是“分享目标的文本匹配”。微信在分享面板里显示的名字可能是“微信”或“WeChat”国内版一般显示“微信”。但是系统分享面板有时候还会把最近使用的人或者“更多”按钮混进来By.textContains(微信)可能同时匹配到多个节点。这时候强烈建议用By.clazz(android.widget.TextView).textContains(微信)缩小范围或者先findObject再判断childCount做多节点处理。另外device.pressBack()之后不要立刻切回 Espresso 的onView因为微信页面退出动画还没结束应用主线程可能也在处理回调。我会在返回之后调用device.waitForIdle()再切回 Espresso必要时再加一个Espresso.onIdle()确保同步。这块的稳定性直接影响整套用例的通过率值得多花一点心思。3.4 混合用例模板在 Espresso 测试中无缝调度 UI Automator最实用的做法不是让一个测试类只属于某一个框架而是两者混用。我的习惯是能用 Espresso 的地方全部用 Espresso只有出现系统窗口或第三方页面的时候才把UiDevice拉出来临时接管。我把自己常用的模板固化成了一个基类里面封装了权限处理和通用点击方法业务测试类继承它就能同时使用两个框架的能力abstract class BaseSystemUiTest { protected val device: UiDevice by lazy { UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) } Rule JvmField val activityScenarioRule ActivityScenarioRule(MainActivity::class.java) protected fun grantRuntimePermissionByUserDialog( permissionText: String 允许 ) { val allow device.wait( Until.findObject(By.text(permissionText)), 8_000 ) if (allow ! null allow.isEnabled) { allow.click() } } protected fun clickSystemText(text: String, timeout: Long 5_000): Boolean { val target device.wait(Until.findObject(By.text(text)), timeout) return if (target ! null) { target.click() true } else { false } } protected fun assertSystemTextDisplayed(text: String, timeout: Long 5_000) { val target device.wait(Until.findObject(By.text(text)), timeout) assertNotNull(系统界面未找到文本: $text, target) } }子类写用例时就非常丝滑class ShareAndPermissionTest : BaseSystemUiTest() { Test fun registerAndShare() { // 注册流程表单填写、按钮点击全部用 Espresso onView(withId(R.id.et_username)).perform(typeText(tester)) onView(withId(R.id.et_password)).perform(typeText(123456)) onView(withId(R.id.btn_register)).perform(click()) // 权限弹窗切到 UI Automator grantRuntimePermissionByUserDialog(允许) // 继续用 Espresso 操作注册成功后的界面 onView(withId(R.id.btn_share)).perform(click()) // 跨应用分享交给 UI Automator clickSystemText(微信) clickSystemText(发送) device.pressBack() device.waitForIdle() // 最终断言还是回到 Espresso onView(withId(R.id.tv_result)).check(matches(withText(已完成))) } }这样混用的好处是逻辑读起来很清楚业务操作是 Espresso 的活系统交互是 UI Automator 的活各干各的。千万不要在一个方法里反复在两个框架之间切来切去因为 Espresso 的同步机制会因为你手动操作了系统组件而出现“等待超时”的错误。3.5 两个框架的等待策略对比Espresso 的自动同步是它最受好评的特性它靠IdlingResource去判断主线程和消息队列是不是空闲空闲了才继续执行。这个机制让测试代码几乎不用考虑“等待加载完成”这件事。UI Automator 没有这个能力它的查找是基于当前已渲染的窗口树。如果界面还在加载你调用findObject可能立即返回 null。所以 UI Automator 必须显式用wait(condition, timeout)。Until.findObject(selector)是轮询机制在超时时间内反复查找找到就返回这样比直接findObject加Thread.sleep高效得多。我的经验是处理系统弹窗时等待时间至少 5 秒起步处理跨应用跳转时等到第三方页面真正出现还要 10 秒尤其是第一次启动微信这种大应用。如果超时设得太短用例可能没问题只是环境慢等到后面就会出现“按钮还没出现”的误报。如果超时设得太长整个测试套件的执行时间又会被拖累。折中方案是把超时值抽成常量在 CI 配置里用构建参数去覆盖针对不同性能的测试机动态调整。4. 常见问题与排查实录4.1 找不到控件先 dump UI 再谈定位UI Automator 找不到控件是所有问题里最常见的。而我排查这类问题从来不靠猜第一步永远是 dump 当前界面adb shell uiautomator dump /sdcard/window_dump.xml adb pull /sdcard/window_dump.xml ./window_dump.xml拉下来用编辑器直接搜文本关键字或资源 ID就能清楚地看到控件到底在不在当前窗口、是不是因为文本被折叠了、资源 ID 是不是带上了进程名这些信息一眼就能看出来。有几个让我印象深刻的翻车原因资源 ID 在同一个屏幕上有多个重复节点。比如 RecyclerView 列表里有多条相同 ID 的 itemfindObject找的是第一个但你要点的可能是第二个。解决办法是结合By.text和By.res一起过滤或者用device.findObjects(selector)遍历判断位置。控件在 WebView 内部。UI Automator 看 WebView 的时候有时候能看到文本但拿不到内部资源 IDBy.text也可能因为content-desc为空而失效。这种情况可以退而求其次用坐标点击或者调整 WebView 本身让控件可访问。控件被系统弹窗盖住。窗口树里确实有该节点但被上层窗口遮挡点击时落到别的地方。这时先处理遮挡窗口再点击原目标。文本是 Android 的 SpannableString显示的内容和树里的 text 不完全一致By.textContains(确定)都匹配不上。这也是为什么断言和点击都建议写一个“别名”匹配函数把常见的“允许 / 始终允许 / 仅在使用期间允许”归到一个集合里。4.2 点击了但没生效等待策略才是真问题很多新手会用findObject找到控件后就立刻点击结果发现没反应。其实最可能的原因不是按钮坏了而是窗口还没完全稳定。比如系统权限弹窗的按钮在弹窗出现动画期间就已经可以被查找但点击命中区域还没有完全落到按钮上最终点击被系统忽略。我的经验是wait到目标之后再额外做一层“可点击状态”确认。val target device.wait(Until.findObject(By.text(允许)), 8_000) if (target ! null !target.isEnabled) { device.waitForIdle() } target?.click()第二个常见原因是查找的 selector 命中了父容器而不是真正可点击的子控件。比如By.textContains(允许)可能命中一个外层 LinearLayout 或 TextView虽然也响应点击但点击区域可能很怪。如果遇到这种“看起来点了但没反应”的现场先看uiAutomator dump出来的节点有没有clickabletrue或者尝试By.clickable(true).text(允许)精确匹配。第三点击坐标的问题。UI Automator 的click()是点击控件中心点但某些 ROM 的二级权限弹窗里同样的文本被两个层叠窗口共享了一个节点中心点可能是透明区域。这种情况我会强制改用clickTopLeft()或干脆用坐标val bounds target.visibleBounds device.click(bounds.centerX(), bounds.centerY())4.3 Espresso 和 UI Automator 来回切换导致找不到 View混合使用两个框架时我踩过最难受的一个坑是UI Automator 操作完系统 UI 后切回 Espresso 时onView报“No views in hierarchy matching”或者直接卡在等待主线程空闲那一步。原因是 UI Automator 的操作可能让被测应用的主线程进入了某个持续的动画或循环状态Espresso 的同步机制认为主线程一直不空闲所以它不敢继续执行。这在前面的跨应用分享用例里最容易出现——从微信返回后被测应用可能在做一个较长动画。解决办法有几个层次。最粗暴但不推荐的做法是加Thread.sleep这会把所有用例的稳定性拖垮。推荐做法是先device.waitForIdle()让 UI Automator 层面的操作稳定然后用Espresso.onIdle()强制等待 Espresso 的 Idle 状态。如果还不行可以InstrumentationRegistry.getInstrumentation().waitForIdleSync()。另一个更隐蔽的问题是UI Automator 操作期间如果被测应用弹出了一个还未处理的对话框Espresso 恢复执行时会认为当前焦点不对导致后续onView找不到匹配目标。排查这种问题还是先 dump UI看看当前堆栈到底停在哪里而不是瞎调等待时间。4.4 国产 ROM 的权限弹窗差异兼容方案真机上跑测试的朋友一定会遇到这种情况同一条权限弹窗处理逻辑在模拟器上没问题到了小米手机上就找不到“允许”按钮到了华为手机上弹窗里可能有两个“允许”一个是“仅本次允许”一个是“始终允许”。我习惯把常见 ROM 的权限按钮文本和资源 ID 沉淀成一组配置系统/ROM常见按钮文本常见资源 ID原生 Android 8-10允许com.android.packageinstaller:id/permission_allow_button原生 Android 11仅在使用期间允许 / 允许com.android.permissioncontroller:id/permission_allow_foreground_only_button小米 MIUI允许 / 仅在使用中允许多基于文本匹配资源 ID 不稳定华为 EMUI允许 / 始终允许多基于文本匹配资源 ID 不稳定基于这些配置我有一个PermissionDialogHelper类维护一个“允许”同理匹配列表按优先级尝试val allowTexts listOf( 允许, 仅在使用期间允许, 始终允许, Allow, While using the app ) fun tryClickAllow(): Boolean { val device UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) for (text in allowTexts) { val target device.wait(Until.findObject(By.text(text)), 3_000) if (target ! null) { target.click() return true } } return false }不过也要小心不同厂家的文本排列不一致比如小米的“仅在使用中允许”和我列表里的“仅在使用期间允许”可能对不上所以一定要结合自己的测试设备去调整列表。这个过程没有捷径只能靠跑测试时不断把真实弹窗 dump 出来补充进去。5. 我的一点实战体会写到最后分享几条我自己的经验。先跑模拟器再跑真机。UI Automator 的很多问题在模拟器上不会暴露比如 ROM 定制弹窗、输入法遮挡、悬浮窗覆盖。我现在的流程是开发环境先用模拟器把逻辑跑通再拿到常用真机矩阵上去跑一遍兼容重点看权限弹窗和分享跳转这两个高发区。第二个经验是把 UI Automator 的 selector 尽可能抽成常量或配置类。不要散落在测试代码里到处写死文本要不然行情一变几个 ROM 一升级你就得满项目找字符串改。集中管理之后维护成本会低很多。第三个经验是不要迷信坐标点击。UI Automator 的坐标点击看起来实在但屏幕尺寸一变代码就废。能用By.res就用资源 ID能用文本就用文本实在不行才用坐标而且坐标要基于屏幕宽高做比例换算。第四个经验是搭一个“失败时自动 dump 当前 UI”的机制。我是在测试基类里通过Rule的方式在测试失败时自动执行uiautomator dump并附带截图这样 CI 上出问题的时候可以直接看失败现场而不是反复猜测。这个习惯帮我省了大量排查时间。最后说一点UI Automator 与 Espresso 结合这件事本质上不是技术难题而是测试策略问题。两个框架各自擅长不同的领域组合使用才能把 Android UI 测试的覆盖范围拉到系统级和跨应用级。先把依赖和基础封装搭好再从小场景一点点接入稳定性慢慢就能养起来。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号