uiautomatorviewer 二次开发之自动生成控件定位符

转载地址:https://testerhome.com/topics/2632


uiautomatorviewer二次开发之自动生成控件定位符

前言
我们在使用Appium进行移动自动化测试脚本编写的时候,经常出现控件无法定位,如ListView下面的item,控件基本属性一样的、某些控件没有id、name等,这个时候,如果单纯靠id、name、text可能无法完全唯一定位一个控件,这个时候就需要编写xpath了,可是xpath语法、写法对于没有接触过的测试人员来说,又是个门槛,接下来,我们就来讨论如何通过二次开发uiautomatorviewer自动生成xpath供用户直接copy使用。
原理
uiautomatorviewer是android SDK包中原生的开源工具,给用户提供一种查看当前终端布局、控件属性的一个辅佐工具,
该工具的GUI是使用RCP组件进行开发的,然后通过uiautomator dump把当前终端布局文件dump到本地,uiautomatorviewer
通过xml布局文件,构造一棵tree,放到Canvas SWT组件中,和当前png截图叠加在一起,同时监听鼠标move等事件,自动
获取该tree的node节点,并且把该node节点的所有属性获取显示出来。


代码结构

com.android.uiautomator:存放uiautomatorviewer工具的GUI界面代码,其中主入口UiAutomatorViewer.java文件里面有main函数入口,工具的窗口就在此创建。
com.android.uiautomator.actions:存放所有anction操作,如:Device screenshot 、open等。
com.android.uiautomator.tree:存放tree封装,dump出来的xml解析成一棵完整的tree,这个包是核心包。

二次开发
首先,dump出来的xml文件被uiautomationviewer解析成自定义的tree,每个节点代表一个控件,所以,如何添加Xpath属性呢?只需要在node节点中添加一个字段即可,其实很简单。通过阅读代码,在com.android.uiautomator.tree包下,有个node节点封装类,UiNode.java,看下以下代码片源:

public class UiNode extends BasicTreeNode {
    private static final Pattern BOUNDS_PATTERN = Pattern
            .compile("\\[-?(\\d+),-?(\\d+)\\]\\[-?(\\d+),-?(\\d+)\\]");

    private final Map<String, String> mAttributes = new LinkedHashMap();
    private String mDisplayName = "ShouldNotSeeMe";
    private Object[] mCachedAttributesArray;

    public void addAtrribute(String key, String value) {
        this.mAttributes.put(key, value);
        updateDisplayName();
        if ("bounds".equals(key))
            updateBounds(value);
    }

    public Map<String, String> getAttributes() {
        return Collections.unmodifiableMap(this.mAttributes);
    }

    private void updateDisplayName() {
        String className = (String) this.mAttributes.get("class");
        if (className == null)
            return;
        String text = (String) this.mAttributes.get("text");
        if (text == null)
            return;
        String contentDescription = (String) this.mAttributes
                .get("content-desc");
        if (contentDescription == null)
            return;
        String index = (String) this.mAttributes.get("index");
        if (index == null)
            return;
        String bounds = (String) this.mAttributes.get("bounds");
        if (bounds == null) {
            return;
        }

        className = className.replace("android.widget.", "");
        className = className.replace("android.view.", "");
        StringBuilder builder = new StringBuilder();
        builder.append('(');
        builder.append(index);
        builder.append(") ");
        builder.append(className);
        if (!text.isEmpty()) {
            builder.append(':');
            builder.append(text);
        }
        if (!contentDescription.isEmpty()) {
            builder.append(" {");
            builder.append(contentDescription);
            builder.append('}');
        }
        builder.append(' ');
        builder.append(bounds);
        this.mDisplayName = builder.toString();

private final Map<String, String> mAttributes = new LinkedHashMap();UiNode 节点下定义一个mAttributes LinkedHashMap,用于存储节点所有key-value属性,如:className、text 、index等等,所以,只需要在这个UiNode 类下添加一个获取xpath方法,如下:

    public String getXpath()
    {
        String className=getNodeClassAttribute();
        String xpath="//"+className;
        String text = getAttribute("text");
        if(text !=null&& !text.equals(""))
        {
            xpath += "[@text='"+text+"']";
            return xpath;
        }else 
        {
            return getAttribute("content-desc") !=""?
                    xpath+"[@content-desc='"+getAttribute("content-desc")+"']"
                    :xpath+"[@index='"+getAttribute("index")+"']";
        }


    }

根据约定的优先级,进行筛选(text>content-desc>index),方法定义完毕后,如何触发getXpath()代码呢?
同样,在此包下UiHierarchyXmlLoader.java中,该类是用于处理把dump xml转换为BasicTreeNode对象,UiHierarchyXmlLoader 引用org.xml.sax.helpers处理基本xml文件(Default base class for SAX2 event handlers.),实现了ContentHandler接口下的startElement、endElement接口,

            public void endElement(String uri, String localName, String qName)
                    throws SAXException {
                if (this.mParentNode != null) {
                    this.mWorkingNode = this.mParentNode;
                    this.mParentNode = this.mParentNode.getParent();
                    `mTmpNode.addAtrribute("xpath",mTmpNode.getXpath());`
                }
            }
        };

到这里,xpath就会自动出现在uiautomatorviewer界面上了,效果如下:


补充
其实uiautomatorviewer二次开发还不止这些,我们可以在uiautomatorviewer中加入录制自动生成java、python等appium脚本,还可以每次用户点击uiautomatorviewer界面,同步刷新(目前需要用户手动点击device screenshot 这个action)等等,如下:

第一次发分享贴,写的不好,希望大家多多鼓励,后续会继续分享
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值