ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

UI Automator Viewer实战:Appium元素定位全攻略

UI Automator Viewer实战:Appium元素定位全攻略 1. 先讲明白UI Automator Viewer在Appium自动化测试里的分工做过Appium自动化测试的应该都体会过一件事写用例的第一步往往不是写代码而是“找到元素”。定位不到元素后面所有断言、点击、输入都是空中楼阁。而在Appium生态里UI Automator Vieweruiautomatorviewer大概是很多测试开发最早接触、也最被低估的一个元素定位工具。它其实是Android SDK自带的一个图形化工具不需要额外安装什么Python库或Node模块只要你的电脑上装了完整的Android SDK在tools/bin或tools目录下就能找到它。它的核心作用就一句话把当前设备或模拟器屏幕上的界面截图出来同时把界面背后的控件层级结构XML Hierarchy解析出来让你看到每个控件暴露了哪些属性——resource-id、class、text、content-desc、bounds这些而这些属性恰好就是你写Appium定位表达式时的“原材料”。听完你可能觉得这工具也太简单了就是个截图加属性查看器。但恰恰是这种简单让它比很多看起来很厉害的商业化工具更适合当作入门第一课。它没有复杂的配置项不需要启动独立的服务端不需要和你项目里已有的Appium环境版本做什么联动校验打开即用。对刚接触Appium的人来说这套“轻量、直观、零依赖”的特性非常重要。另外也多说一句后来有很多人直接跳到Appium Inspector或者Weditor跳过了UI Automator Viewer。我的建议是别跳。Appium Inspector功能确实更全但你一开始就用Inspector很容易忽略对Android控件层级本身的理解。UI Automator Viewer那种“原始感”恰恰能逼你老老实实看懂XML节点树看懂一个控件在层级结构里的位置这种基本功在后面排查复杂页面问题时特别值钱。2. 启动这个工具之前环境准备里有三个容易忽略的细节很多人卡在“双击打开之后连不上设备”这个第一步上然后就以为工具坏了。其实UI Automator Viewer本身不太容易坏大多数启动问题都出在准备环节。**第一个细节确认你的启动路径。**新版Android SDK里uiautomatorviewer位于SDK目录下的tools/bin文件夹文件名是uiautomatorviewer.batWindows或uiautomatorviewermacOS/Linux。老版本SDK则直接放在tools目录下是uiautomatorviewer.sh或uiautomatorviewer.bat。如果你在Android Studio的SDK Manager里连Platform-Tools一起装了一般都能找到。实在找不到就用命令行敲一下Mac和Linux执行uiautomatorviewerWindows执行uiautomatorviewer.bat。**第二个细节Java环境变量必须配好。**别笑这个问题到现在还经常有人踩。UI Automator Viewer本质上是一个Java Swing桌面应用靠的是本地Java环境解析并渲染XML层级。如果你的JAVA_HOME没配或者配的是JRE而不是JDK启动的时候要么没反应要么报一个找不到类的错误。我的建议是直接用命令行启动这样任何报错都能在控制台里看到比双击bat文件失败后光看一个弹窗消失要直观得多。**第三个细节设备和工具的连接状态。**在启动UI Automator Viewer之前先确保设备或模拟器已经处于可调试状态。对真机来说要打开开发者选项里的USB调试并且建议用adb devices确认设备状态是device而不是offline或unauthorized。很多人在Windows上还会遇到设备驱动没装好的情况表现就是adb devices里能看到设备号但状态是offline这种时候不是UI Automator Viewer的锅是驱动或者USB接口的问题换个USB口或者重装驱动通常能解决。等设备确认好了启动UI Automator Viewer点击右上角的Device Screenshot按钮一个手机图标如果顺利就能看到当前屏幕截图和节点树了。如果点完之后一直在转圈最后报错“Error while obtaining UI hierarchy XML file”多半就是设备断开了或者当前屏幕正好在播放动画、有弹层频繁刷新导致系统拿不到稳定快照重新点一次通常就能好。3. 界面三件套截图区、层级树、属性面板的配合逻辑UI Automator Viewer的主界面打开之后很多新手会有点懵因为左右两边都是密密麻麻的信息。但其实它就是一个很标准的“三栏工作台”左边是截图区右边是节点树下方是属性面板。截图区就是你当前设备屏幕的真实快照。当你点击节点树里的某一个节点时截图区对应控件的位置会用红色边框高亮出来。反过来你在截图区点击屏幕上的某个位置节点树会自动定位到命中的那个节点。这个双向联动非常关键新手一定要养成“先看截图、再点节点树、再看属性”的习惯而不是一头扎进节点树里盲猜。节点树展示的是Android系统当前的View层级结构。我强烈建议新手在刚开始用的时候把节点树想象成一份“App界面的目录树”。页面上你看到的每一个控件——按钮、输入框、图片、文字包括那些看不见但占位的ViewGroup——都会在这棵树里有一个对应的节点。层级越深说明布局嵌套越复杂。有的App用了一些自定义View节点树里可能只有一个笼统的节点属性也很少这种时候就说明这个控件不是一个标准控件定位的时候要借助父节点或兄弟节点来辅助锁定。属性面板是最终要重点看的地方。点中一个节点后下方会列出它的全部属性包括index、text、resource-id、class、package、content-desc、checkable、checked、clickable、enabled、focusable、focused、scrollable、long-clickable、password、bounds这几个常见项。这些属性里真正在写Appium定位表达式时高频使用的是resource-id、content-desc、text、class、bounds这五个其他的更多是辅助判断控件状态。举一个具体的例子。你要定位一个登录按钮在节点树里点中它之后属性面板会显示index: 2 text: 登录 resource-id: com.example.app:id/btn_login class: android.widget.Button package: com.example.app content-desc: bounds: [180,1200][900,1320]这一串信息换成Appium定位表达式就是by_id(com.example.app:id/btn_login)或者driver.findElement(By.id(com.example.app:id/btn_login))。如果resource-id为空再看class和text或者用text来定位。如果text也没有但content-desc有值那就用accessibility id。总之写的定位表达式完全可以从属性面板里的字段直接映射过来。这里有一个实操中特别有用的习惯拿到一个页面之后不要急着写代码先在UI Automator Viewer里把整个页面从上到下点一遍把你关心的控件全部看一遍属性心里有个底再动手。这一步看起来多花了三五分钟但能省下后面调试定位表达式的大量时间。4. 属性面板逐行拆解从界面上的一串字符到可用的定位表达式很多教程会直接告诉你“resource-id是首选定位方式”但没有解释为什么是首选、什么情况下优先级要换。UI Automator Viewer最大的价值就是让你直观看到这些属性在不同控件上的真实分布从而自己总结出定位策略而不是死记规则。**resource-id控件身份证但经常缺席。**resource-id是Android开发者在布局文件里给控件指定的id理论上应该是整个App内唯一的。但做过实际项目的人都知道很多App的页面是由服务端动态下发的或者列表里的Item复用了同一个布局导致页面上大量控件的resource-id完全相同。你在UI Automator Viewer里会看到三四行的resource-id都叫com.example.app:id/feed_item_title这时候如果还坚持只用id定位要么定位到第一个要么报错说找到多个元素。这种场景下正确的做法是结合index或者在层级树中的位置来缩小范围。**text中文App里最直观的锚点。**对中文应用来说text属性经常是最好用的直接用页面上的文案去定位读代码的人一看就懂。但它的局限也很明显——用户的语言环境变了text可能就跟着变了。如果App做了多语言适配测试环境的语言设置不一样你写的中文text定位表达式就失效了。所以我在实际项目中通常把text作为备选方案或者在knowing页面文案一定不会变的前提下才用。**content-desc无障碍属性也是定位利器。**Android系统里content-desc原本是为无障碍服务设计的屏幕阅读器会朗读这段描述给视障用户。对自动化测试来说这个属性反而经常比text更稳定因为开发手写content-desc的概率低于text一旦写了通常是有意为之而且基本不会变成用户可见文案。如果UI Automator Viewer里显示某个按钮的content-desc不为空那用Appium的accessibility id定位它是最省心的方案写法是driver.findElementByAccessibilityId(xxx)。但要注意iOS里的accessibility id概念和Android的content-desc类似但不完全一样跨平台写用例的时候要小心这个差异。**class控件类型的身份标签。**class属性标识的是控件对应的Java类比如android.widget.Button、android.widget.EditText、android.widget.ImageView、android.widget.TextView。在UI Automator Viewer里class是最能帮你快速判断控件类型的信息。有时候遇到自定义控件class会是一个很长的自定义类名后面还跟着包名这种控件在XPath定位里可以直接按class名匹配但因为类名太长XPath写起来又长又丑不如用父节点加子节点的相对关系去定位。**bounds坐标系里的四个数字。**bounds的格式是[x1,y1][x2,y2]表示控件左上角和右下角的坐标。范围通常是屏幕分辨率大小比如1080x2400的屏幕x2最大值就是1080y2最大值是2400。这个属性在UI Automator Viewer里看起来很简单但它解决了一个很实际的问题当resource-id、content-desc、text全都没有的时候你只能退而求其次用坐标定位或者用XPath里基于坐标的谓词比如contains(bounds, [180,1200])来锁定元素。另外bounds也是以后你判断一个元素是否在可视区域内的重要依据——如果一个元素的y2坐标小于0那它在当前屏幕上肯定不可见这时对它执行点击操作Appium会报错。我在团队里经常说一句话属性面板就是Appium定位表达式的对照表。你在UI Automator Viewer里抄下来的每一个字段最后都能在代码里找到对应的位置。理解了这点你就算是真正把这个工具用明白了而不是停留在“能打开、能截图”的程度。5. 完整走一遍实战模拟器里从打开App到写出定位语句前面讲了那么多原理和细节这一节我们完整走一遍流程。我这边就用一个常见的模拟器环境目标App是一个标准的登录页面要完成的任务是定位用户名输入框、密码输入框和登录按钮并写出对应的Appium定位语句。第一步是启动模拟器。无论是Android Studio自带的AVD还是第三方模拟器确保它已经开机并且处于解锁状态。在命令行执行adb devices能看到emulator-5554 device这样的输出说明设备连接正常。如果模拟器处于锁屏界面UI Automator Viewer抓到的就是锁屏的层级不是App的层级很多新手在这里卡住以为工具坏了其实是没解锁。第二步是打开目标App。这里可以用手动点击图标的方式也可以直接通过adb命令启动。使用adb shell am start -n 包名/Activity名可以快速拉起指定的Activity。如果你一时记不住包名和Activity名可以先在模拟器里手动打开App然后执行adb shell dumpsys window | grep mCurrentFocus输出的信息里通常包含当前聚焦窗口的包名和Activity名比如com.example.app/.MainActivity直接把这串填进去就行。第三步是启动UI Automator Viewer并抓取界面。在SDK的tools/bin目录下执行uiautomatorviewerWindows下是uiautomatorviewer.bat等窗口弹出来后点击左上角的手机图标按钮等待几秒钟界面截图和XML层级就会显示出来。第四步是在节点树中定位目标控件。假设我们要定位用户名输入框先看截图区点击屏幕上对应的输入框位置节点树会自动选中一个节点然后看属性面板里面可能会是这样text: 请输入用户名 resource-id: com.example.app:id/et_username class: android.widget.EditText这个控件的text通常是hint提示文字不是用户真正输入的内容但用来做定位完全没有问题因为在输入之前的初始状态下text属性就是hint的值。定位表达式可以写成driver.findElement(By.id(com.example.app:id/et_username))密码框类似关注它的resource-id是不是com.example.app:id/et_password同时注意属性面板里password这一项是否显示为true如果是说明控件确实被标记为密码输入框text属性一般不会返回真实值所以密码框的定位不建议依赖text优先用resource-id。登录按钮通常会显示为android.widget.Buttonresource-id可能是com.example.app:id/btn_logintext是“登录”。如果这个按钮同时存在text和resource-id通常优先用resource-id理由前面讲过稳定性更高。通过这一套流程你已经能完成最基础的“看一眼、写一行”的定位操作了。多练习几次之后你会发现效率会快不少因为整个操作已经内化成一种条件反射点截图、看节点、抄属性、写表达式。6. 踩坑实录用了这么多年五个高频问题与排查思路工具用久了总会遇到各种莫名其妙的情况。下面这几个坑是我和团队在实际项目里反复遇到过、并且在网上也很难一次搜到完整解决方案的整理出来供大家参考。**第一个坑连接真机后截图一直失败报Error while obtaining UI hierarchy XML file。**首先要排除设备是否真的连接成功执行adb devices看状态。然后确认当前有没有别的地方正在占用uiautomator服务。这个报错在Windows上特别常见原因是uiautomatorviewer连接设备时走的是ADB的某个内部端口如果这个端口被占用了比如开启了多个版本的Appium服务就有可能会冲突。处理办法是重启adb服务执行adb kill-server然后adb start-server再重新连接设备点击截图。如果还不行检查一下设备上的“开发者选项 — 指针位置”和“显示布局边界”这类调试辅助功能是否打开了理论上不影响但实测中打开了这类辅助功能会让UI层级里多出一些额外节点干扰截图过程。**第二个坑节点树里能看到节点但点击之后对应的控件在截图区没有高亮。**这个问题大部分时候不是工具坏了而是你点击的节点是一个没有实际绘制区域的ViewGroup或者它的宽度和高度都是0在截图区自然就没有可以高亮的位置。这种情况通常发生在一些自绘组件或动态布局里节点的属性面板也能看到bounds显示为[0,0][0,0]。处理思路是往上一层或者往下一层找找到真正有宽高的子节点再去看它的属性。**第三个坑webviewH5页面里的元素在UI Automator Viewer里看不到。**这一点特别重要也是很多从Selenium转过来的人容易踩的坎。UI Automator Viewer属于原生Android测试工具链它只能看到原生View层级里的控件。如果App里嵌入了WebView并且页面内容是HTML页面比如很多电商App的营销页你打开UI Automator Viewer去看大概率只能看到整个WebView容器那一个节点class显示为android.webkit.WebView至于WebView内部的按钮、输入框、下拉框——那些在网页DOM里对应的HTML元素——这个工具是看不到的。这种情况下想定位元素正确的方向是切换到Chrome DevTools协议通过chrome://inspect去调试WebView内部页面或者使用Appium的context切换切成WEBVIEW的上下文之后用Selenium风格的选择器来定位。顺便说一句热词里提到的“selenium定位获取下拉框元素不是原生下拉框是divulli组合”就属于这类场景的代表。遇到这种H5页面的div/ul/li结构下拉框不能指望UI Automator Viewer能给你原生控件的属性它根本不在原生View树里。**第四个坑原生下拉框和自定义PopupWindow在UI Automator Viewer里节点层级特别深找不到真正要点的选项。**原生下拉框比如android.widget.Spinner点开后弹出的选项列表在UI Automator Viewer里通常会挂在一个弹出窗口的节点下。实际抓取时你会发现如果你在Spinner弹开的那一瞬间去抓页面弹出的列表项是可以看到的class通常是android.widget.TextView或android.widget.CheckedTextView。但如果弹窗的层级比较深节点树一层一层展开比较费劲有个小技巧是直接在截图区点击那个弹出的选项让UI Automator Viewer自动定位到对应节点然后看属性。对于用PopupWindow自制的下拉菜单原理一样只要它能显示在屏幕上并且在原生View树里它就一定能被UI Automator Viewer抓到只是层级深而已不要被吓到耐心展开就能找到。**第五个坑页面有动态刷新或者弹层时UI Automator Viewer抓到的界面和当前界面差了一拍。**这个坑非常烦人表现形式是你看到UI Automator Viewer里的截图和手机上真实的实时界面不一致。原因不难理解UI Automator Viewer抓取层级的时间点和你目光落在截图上的时间点有延迟如果页面在这中间刷新了自然就错位了。应对办法是尽量在页面静止状态下去抓取如果需要抓取弹窗内容先用代码或者手动操作把弹窗打开等页面完全稳定再点击截图。还有一个土办法就是多抓两次对比如果两次抓结果一致基本可以认定当前页面的层级是稳定的。踩过这些坑之后再回头看UI Automator Viewer确实不够“智能”它只是一个朴素的窥探工具不会替你分析不会给你推荐最合适的定位方式它只是把Android原生界面的真实模样原原本本展示出来。但也正因为这样用它能培养出很重要的元素定位直觉拿到一个App页面下意识就会去猜它的布局层级大概什么样哪些控件有id哪些控件只有text哪些控件压根什么都没有。这种直觉在现在各种复杂App横行的环境里反而成了比工具本身更稀缺的能力。另外如果你已经用了一段时间UI Automator Viewer我建议可以拿它和Appium Inspector做一个串行对比用同一个页面分别在这两个工具里看一遍。你会发现Appium Inspector除了能看到原生控件还能直接展示XPath、支持原生页面和WebView的切换还能直接和Appium服务端连在一起做录制操作。它确实更强大但当你在复杂页面上定位不到元素时回来用UI Automator Viewer验一眼原生层级往往能更快找到问题所在——因为原生层级的真相就摆在那里不会因为工具的包装而改变。
返回列表