代码改变世界

类jQuery selector的控件查询iQuery开源类库介绍

2012-08-14 16:08  知平软件  阅读(3269)  评论(0编辑  收藏  举报

整个iQuery的代码是开源的,今后也将有后续文章讲解iQuery的实现方式,对iQuery有兴趣的朋友可以在此下载或者同步代码:

https://github.com/vowei/iQuery

在程序界面(UI)自动化测试中,最烦人的就是抓取控件的过程了,要么是程序员忘记在代码里给控件添加自动化测试用的标签;要么就是界面布局经常变,如果是基于坐标位置抓取的话,一点小变化都很麻烦。碰到这种问题很讨厌,界面变更导致的测试失败由于跟产品问题无关,因此开发一方经常会拒绝修改代码,问题全部丢给测试工程师一方。而在手机应用上,这种问题就更突出了,一个市场占有率较广的应用通常都支持好几个平台,而各个平台的编程方式还不一样……

iQuery的思路借鉴自jQuery(或者说是CSS)的选择器,一方面提供一个比较简单的方式在自动化测试程序里抓取控件,一方面试图通过将控件归类提供一种跨平台统一的控件抓取表述语法。它是一个多平台通用的控件查询语法,当前已经实现了iOS版,支持在Instrument里使用,本文介绍iQuery在iOS上Instrument中的用法。

我们尽量将iQuery的语法与jQuery的语法保持一致,然而由于二者的目标不一致(jQuery致力于尽可能多的抓取控件,而iQuery则致力于以最快的速度抓取目标控件),所以iQuery和jQuery的语法有一些细微的差别。

我们以下面的程序为例说明iQuery的语法和在iOS上的用法:


 TestUIDemo_iOS

其对应的控件树形式为,控件树和后面的示例图片都是从HierarchyViewer-4-iOS上获取的:
HierarchyViewer-4-iOS工具的使用说明请参见文章:

http://www.cnblogs.com/vowei/archive/2012/08/13/2614468.html
其源代码也是开源的:
https://github.com/vowei/HierarchyViewer-4-iOS


 TestUIDemo_ControlTree

下面是几个iQuery语法示例,更详细的示例以及文档请参考网页http://www.vowei.com/iquery-1.html):

要在Instrument中使用iQuery,只要在自动化测试用例的脚本中引入iquery.js文件即可,如下所示:

在后面的例子中,下面root和assert是两个全局变量,root表示当前应用的主窗口。

var root = target.frontMostApp().mainWindow();
var assert = new Assert();

跟jQuery类似,iQuery的查询操作函数名也是$,函数声明如下:

$(selector, context)

1、跟jQuery类似,iQuery也可以省略查询起始的控件位置(即context参数),那样的话默认从应用的当前窗口开始查询:

test("([attr (>|<|>=|<=) float])比较属性值", function() {
    var query = "> [value >= 59%]";  
    var result = $(query);
    assert.Equals(1, result.length);

    query = "> [:height > 31]";
    result = $(query);
    assert.Equals(6, result.length);
    for ( var i = 0;i < result.length; ++i ) {
    assert.True(result[i].rect().size.height > 31);
}

    query = "UIAWindow > UIASegmentedControl > UIAButton :eq(0) + UIAButton";
    result = $(query);
    assert.Equals(1, result.length);
    assert.Equals("Second", result[0].name());
});

上例中:
$("> [:height > 31]")的意思在默认的当前界面主窗口下,在其子控件列表里查找高度大于31个像素点的控件,其中我们叫它:height 伪属性,所有在方括号内,以英文冒号开头的都被称作伪属性,为了统一表示多个平台,有些平台例如Android实现了控件的height属性,而有些平台例如iOS上就没有实现这个属性,是通过rect属性获取的。为了在多个平台上统一表达这些差异,我们将这些差异抽象成伪属性的概念。

$("> [value >= 59%]")的意思在默认的当前界面主窗口下,在其子控件列表里查找具有名为“value”的属性,且其值大于59%的控件。如下图所示:

TestUIDemo_ControlTree_Details

$("UIAWindow > UIASegmentedControl > UIAButton :eq(0) + UIAButton")的意思是在指定的上下文控件(即当前窗口对象)中,先匹配类型名为“UIAWindow”的控件也就是窗口对象本身,在其子控件列表中过滤出类型名为“UIASegmentedControl”的控件集中,找出所有类型名为“UIAButton”的子控件数组,接着在数组中获取第一个子控件(:eq(0))的后面一个名为“UIAButton”的子控件。

TestUIDemo_ControlTree_Details1

2、[iOS]上,针对JavaScript UIA对象,iQuery给每个对象提供了iQuery查询扩展函数:

test("测试对UIAElement的扩展", function() {    
    var result = root.$("> [value >= 59%]");
    assert.Equals(1, result.length);
    root.logElementTree();

    result = root.segmentedControls()[0].$("> :button");
    assert.Equals(3, result.length);
});

上例演示了针对任意一个UIAElement对象,都有一个扩展的iQuery函数$(query)。

3、再来看看指定iQuery查询条件“selector”和查询起始的控件位置“context”的做法:

test("(*)匹配所有元素", function(){
    var query = "> *";
    var result = $(query, root.navigationBars()[0]);
    assert.Equals(2, result.length);
assert.Equals("UIAStaticText", type(result[1]));

    query = ">> UIAStaticText:not([name!='TestUIDemo'])";
    result = $(query, root);
    assert.Equals(1, result.length);
    assert.Equals("TestUIDemo", result[0].name());
});


上例中,“>> UIAStaticText:not([name!='TestUIDemo'])”的意思是从指定的上下文(context)控件root的所有子孙控件集合里,首先过滤出类型名为UIAStaticText的控件列表,再在其中找出所有不满足name属性的值不是“TestUIDemo”的控件,如下图所示:

TestUIDemo_ControlTree_Details2

从前面几个例子里可以看出,iQuery分为这几大部分:
以空格分隔的过滤条件默认是交集查询的方式,即后一个条件在前一个条件过滤后的控件集合里执行过滤操作。这一点跟jQuery的语法是不一致的,在jQuery里,空格分隔的条件是爷孙关系,即前一个条件在爷爷层过滤控件,再在结果控件的子孙控件中匹配空格后的条件。

子孙层次查询,通过“>”、“>>”等操作符指明过滤的层次。
注意:在iOS平台上,instrument下javascript的API在遍历控件树时非常慢,以本文的测试程序为例,从窗口层开始执行全遍历需要2分钟左右,因此除非特别有必要,不建议在iOS平台下使用“>>”操作符。

按照任意的控件类型名查询控件。
注意:在jQuery里,根据控件类型名的查询是在当前context控件的子孙控件集里查询的,由于前面描述的原因,iOS上子孙控件的遍历操作非常慢,因此从代码执行速度方面的考虑,iQuery只在context控件这一层执行查询操作,如果要在子孙控件里过滤,必须使用“>”、“>>”和“> num”操作符指明要查询的控件在控件树里的层次信息。

#id,按照控件的Id查询。
注意:按控件id查询默认是在当前context控件的子孙控件集里查询的,由于前面描述的原因,iOS上子孙控件的遍历操作非常慢,因此除非迫不得已,不建议使用。

伪类型,以冒号开头的类型都是伪类型。iQuery通过将多个平台共有的控件抽象成伪控件,便于对跨平台应用的测试,比如说,几乎所有的操作系统里都有文本框这个概念,因此使用“:text”这个伪类型就可以表达不同操作系统上的文本框了。

属性,根据控件自身的属性值过滤,属性值支持字符串的比较(如 [attr $= ‘value’] 指查询属性“attr”的值以字符串“value”结尾的控件),数字甚至的百分比的比较(如 [attr >= 59%] 指查询属性“attr”的值大于59%的控件)。

伪属性,跟伪类型类似,用来抽象不同平台控件共有的属性,例如表示控件位置的top、left、right、bottom、width和height之类的属性。伪属性支持用户扩展,即可以在测试用例里注册一个新的伪属性,通过提供的自定义过滤函数查询。

最后,希望iQuery能够简化你的自动化测试编写工作,如果有任何建议,请在iQuery的官方文档的评论栏(http://www.vowei.com/iquery-1.html)提出你的宝贵意见。

本文由知平软件 施懿民编写,请关注我们的微博