为UiAutomatorViewer添加xpath支持

UiAutomatorViewer是Android SDK自带的测试工具,用来查看手机或模拟器上的界面元素,小巧,简单,开箱即用,十分方便。美中不足之处在于,它不能获取界面元素的xpath.

写自动化测试脚本时,xpath是一种非常方便的定位方式。Appium等一些成熟的工具框架可以获取到界面元素xpath,但使用起来稍有点重量级。那么是否也可以给UiAutomatorViewer添加xpath支持呢?

答案是肯定的。

首先下载UiAutomatorView源代码,我用的地址是https://android.googlesource.com/platform/frameworks/testing/+/aecdc4a/uiautomator/utils/

代码不多,可以直接组织成一个Java项目,快速上手。如下图,其中image目录在下方,没有列出。

入口类是com.android.uiautomator.UiAutomatorViewer,没什么好说的,不用修改。

主要关注的类是com.android.uiautomator.tree.UiHierarchyXmlLoader,重点是以下代码片断

        DefaultHandler handler = new DefaultHandler(){
            BasicTreeNode mParentNode;
            BasicTreeNode mWorkingNode;
            @Override
            public void startElement(String uri, String localName, String qName,
                    Attributes attributes) throws SAXException {
                boolean nodeCreated = false;
                // starting an element implies that the element that has not yet been closed
                // will be the parent of the element that is being started here
                mParentNode = mWorkingNode;
                if ("hierarchy".equals(qName)) {
                    mWorkingNode = new RootWindowNode(attributes.getValue("windowName"));
                    nodeCreated = true;
                } else if ("node".equals(qName)) {
                    UiNode tmpNode = new UiNode();
                    for (int i = 0; i < attributes.getLength(); i++) {
                        tmpNode.addAtrribute(attributes.getQName(i), attributes.getValue(i));
                    }

UiAutomatorViewer调用安卓命令,生成当前界面的XML,这段代码用来解析XML,生成控件树。hierarchy是根节点,其余节点名称都是node,接下来的for循环里把node的属性存入控件节点。

生成xpath的原理:

1. 若节点resource-id属性非空且唯一,则xpath为 //CLASS[@resource-id=‘...‘],其中CLASS就是节点的class属性,比如android.widget.TextView

2. 若节点text属性非空且唯一,则xpath为 //CLASS[@text=‘...‘]

3. 若节点content-desc属性非空且唯一,则xpath为 //CLASS[@content-desc=‘...‘]

4. 若以上三属性都不唯一,则尝试它们的两两组合是否唯一,比如resource-id和text组合唯一,则xpath为 //CLASS[@resource-id=‘...‘ and @text=‘...‘]

5. 三属性两两组合不唯一,则尝试三者组合是否唯一

6. 以上条件全不满足,则先获取父节点的xpath,再把当前节点拼上去,如://android.widget.ListView[@resource-id=‘android:id/list‘]/android.widget.LinearLayout[6],其中android.widget.LinearLayout[6]代表当前节点,前面的部分则是父节点。这个过程需要以层序遍历控件树,这样当处理一个节点时,可以保证它的父节点xpath已生成完毕。

有一个细节需要注意,安卓设备生成的界面XML中,每个节点有一个index属性,表示它作为父节点的第几个子节点,编号从0开始。需要注意的是,这个index不区分子节点类型。比如,一个父节点有6个子节点,那么无论它们是什么类型,index属性只与它们的次序有关。

但在xpath中确不是这样,比如前面第6条中的例子,//android.widget.ListView[@resource-id=‘android:id/list‘]/android.widget.LinearLayout[6],它表示的是第6个类型为android.widget.LinearLayout的子节点,也就是说xpath中的序号是区分类型的。

为了处理这个问题,我在第一版的实现中,把xpath按这样生成://android.widget.ListView[@resource-id=‘android:id/list‘]/*[6], 也就是说,忽略子节点类型,直接获取第6个,这个序号就是节点的index属性再加1,因为xpath是从1开始编号,而控件树index属性从0开始。

一般情况下,这是可以的,但有少数情况,控件树的index属性不连续,比如第一个子节点index为0,第二个子节点index为2,1被跳过去了。那么当我还是按照前面方法生成xpath时,第二个节点就找不到了。

解决办法还是有的,当一个节点作为子节点被添加到父节点时,检查前面已经添加过几个同类型的节点,根据这个信息,确定自己的编号。为了与index区分,这个“编号”属性取名为classIndex,表示同类型子节点中的序号。比如,现在一个类型为android.widget.LinearLayout的节点被添加到父节点,发现父节点已经有了两个该类型的子节点,同时还有其他类型子节点若干,那么这个新添加的节点编号就是3,即classIndex=3

生成xpath的过程应该在整个XML解析完毕之后再开始,从根节点出发,按层序遍历节点生成xpath和classIndex,并调用UiNode.addAttribute(key, value)添加到节点中。需要注意的是根节点的xpath为:/hierarchy

大功告成,启动程序测试一下效果吧。如下图,右下角即是控件元素对应的xpath

时间: 2024-11-03 22:08:04

为UiAutomatorViewer添加xpath支持的相关文章

为Gradle添加UTF-8支持

gradle默认使用系统字符编码,大多数中文系统是使用GBK编码 但程序员绝大部分都是使用UTF-8写各类java文件以及其他资源文件 编译时很容易报错,比如下面的错误: ”警告:编码 GBK 的不可映射字符“ 有两种办法可以给gradle添加UTF-8支持 第一种,在我们的项目配置文件build.gradle中最后添加下面的语句 tasks.withType(JavaCompile) { options.encoding = "UTF-8" } 这样,这个项目在进行gradle打包时

为UIAlertView添加block支持

系统自带的UIAlertView只能支持delegate方式. 如果你只有一个UIAlertView这种方式可能无关紧要. 但如果你有二个或多个UIAlertView, 你需要在委托方法中进行判断是哪个UIAlertView实例的产生的委托, 接着又要判断是响应哪个button. 如果你曾经这样做过, 想想这是多杂的代码. Objective-C是支持块代码的, 如果对UIAlertView添加块支持, 那岂不是一个美事. 这里推荐一个开源的实现: https://github.com/jiv

Ubuntu下安装VirtualBox并为其添加USB支持

1.下载VirtualBox软件包和USB支持包 下载网址均为为:https://www.virtualbox.org/wiki/Downloads VirtualBox软件包下载项: VirtualBox 4.3.10 for Linux hosts USB支持包下载项: VirtualBox 4.3.10 Oracle VM VirtualBox Extension Pack All supported platforms 2.下载完成后,进行安装 3.添加当前用户到组 sudo gedit

Qt国际化(Q_DECLARE_TR_FUNCTIONS() 宏给非Qt类添加翻译支持,以前没见过QTextEncoder和QTextDecoder和QLibraryInfo::location()和QEvent::LanguageChange)

Internationalization with Qt 应用程序的国际化就是使得程序能在国际间可用而不仅仅是在本国可用的过程. Relevant Qt Classes andAPIs 以下的类支持Qt的国际化. QTextCodec QTextDecoder QTextEncoder QTranslator QLocale Languages and WritingSystems 有时,国际化是比较简单的,例如,把美国的应用程序让澳大利亚或英国的用户可访问,只需要简单的改变拼写.但是,把美国的

在IntelliJ IDEA中添加框架支持时找不到Hibernate的解决办法

问题描述 第一次在Add Frameworks support界面中添加hibernate支持的时候,异常中断,导致没有成功添加. 第二次进入Add Frameworks support窗口时,发现找不到hibernate. 解决办法 打开项目根目录下的spring-mvc-crud.iml文件,搜索hibernate找到这段代码并删除,然后重新添加框架支持即可选择hibernate. <facet type="hibernate" name="Hibernate&qu

为Vim 添加vimgdb支持

为Vim 添加vimgdb支持 1.    下载最新的vim74的源码包 wget ftp://ftp.vim.org/pub/vim/unix/vim-7.4.tar.bz2 2.下载vimgdb-for-vim7.4源码 wget https://github.com/larrupingpig/vimgdb-for-vim7.4/archive/master.zip -O vimgdb-for-vim74.zip 3.解压所有文件,并应用补丁文件 tar xjvf vim-7.4.tar.b

Mybatis添加Ehcache支持

1.Mybatis默认的缓存配置 MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制. Mybatis缓存包含全局的缓存和局部的缓存,全局的缓存可以讲主配置文件的setting属性的参数cacheEnabled设置为true(好吧,默认为true), 局部的二级缓存默认情况下是没有开启的,要开启二级缓存,你需要在你的 SQL 映射文件中添加一行: <cache/> eviction(回收策略) 默认的是 LRU.可选择项有FIFO,SOFT,WEAK flushInte

java-cef系列视频第三集:添加flash支持

上一集我们介绍了如何搭建java-cef调试环境. 本视频介绍如何给java-cef客户端添加flashplayer支持 本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 中国大陆许可协议进行许可.

XUtils BitmapUtils 改造以添加drawable支持

=== XUtilsBitmapUtils 改造以添加drawable支持 === # XUtils 简介 XUtils 是一套少有的早期国产安卓框架, 其源于AFinal, 目录结构也与之相似, 但是代码却进行了大量的重构, 是XUtils更加现代, 解决了AFinal 的OOM等问题. 目前 XUtils 已经支持 API 8(android 2.2) 至 API 21(android 5.0.x). XUtils 主要内置了DbUtils 模块, ViewUtils 模块, HttpUti