转自:http://www.ibm.com/developerworks/cn/rational/r-cn-webtopguitestframework/index.html
近些年来,随着 Web 2.0 技术的大范围普及和广泛应用,在全球范围内出现了众多新颖的激动人心的软件产品或服务。在这些 Web 应用中,有一类主要的分支被称作 Web 桌面应用。Rational Functional Tester(RFT)是 IBM 提供的一款自动化测试工具,适用于各种测试工作,特别擅长于 GUI 方面的自动化测试。
本文借助在 Rational Functional Tester(RFT)平台下设计和开发 LotusLive Meeting 这个自动化测试项目,介绍了如何使用 Rational Functional Tester 设计 Web 2.0 应用的 GUI 自动化测试框架,并介绍自动化测试框架设计和开发中的一些原则和经验。
近些年来,随着 Web 2.0 技术的大范围普及和广泛应用,在全球范围内出现了众多新颖的激动人心的软件产品或服务。在这些 Web 应用中,有一类主要的分支被称作 Web 桌面应用。例如,widgetop、cloudworks、eyeOS、Mygoya、Wixi 等。在 IBM 公司内部很多产品线也开始逐步开发出或者转移到 Web 平台。在 IBM 软件产品和服务中,具有 Web 桌面应用特点的产品也非常的多,像是 Domino iNotes,LotusLive Meeting,Sametime Meeting 等。作为 GUI 自动化软件测试人员或者框架及脚本开发人员,如何找到一种高效、稳定的并且是针对这种软件架构和产品特性的 GUI 自动化测试方法就显得尤为重要了。
Rational Functional Tester(RFT)是 IBM 提供的一款自动化测试工具,适用于各种测试工作,特别擅长于 GUI 方面的自动化测试。在 IBM 公司内部的各个测试组,例如 FVT、BVT、GVT,甚至是 SVT 都得到了广泛的应用。
本文借助在 RFT 平台下设计和开发 LotusLive Meeting 这个自动化测试项目,来介绍一下自动化测试框架设计和开发中的一些原则和经验。
随着 Web 开发技术的日新月异,很多 Web 应用都开始越来越注重用户的体验。其中 Web 桌面应用(Webtop)在很大程度上满足了用户日常使用桌面应用的习惯,在众多的新颖的 Web 应用中占有较大的比例。具有 Webtop 特点的 Web 应用一般具有以下的特点:首先从使用者的角度,如果我们没发觉它是运行的浏览器里面,那么它的用户体验更像是传统桌面程序。也就是说在使用过程中不像是其他 Web 应用要在相对较多的浏览器窗口之间或者页面之间进行跳转,而是一直维持着一个相同的浏览器窗口。UI 的变化都发生在同一个浏览器窗口对象内部。另外从技术角度上讲,浏览器窗口内的顶层 DOM 对象基本保持不变,很多 UI 的变化都是刷新或动态加载局部容器的来实现的。对于这类应用的特点,在做自动化测试脚本开发过程中可以加以利用,大大提高脚本的质量和执行效率。
ITCL 框架是 IBM 公司内部被广泛使用的自动化设计框架,也叫做 IBM 框架。主要的设计宗旨就是将代码划分成三层结构,即对象层(Objects layer)、任务层(Task layer)、测试用例层(Testcase layer)。将代码划分成三层结构使得“做什么“和”如何做“分离开来,有利于代码的组织,结构清晰。同时提高了代码的可复用性和扩展性。当使用 ITCL 框架开发自动化测试脚本时,最核心的任务就是合理的设计和组织对象层和任务层。合理的设计对象层和任务层常常会使整个自动化项目的开发和后期维护达到事半功倍的效果。本文的主要目的就是提供一套设计原则以及一些对象是别的经验,针对 Web 桌面应用程序的特点给出对象层设计的解决方案。以提到对象识别的准确性和稳定性,这一点对于 UI 经常变化的软件和 NVL 版本软件的国际化测试有特别重要的意义。
LotusLive Meeting 是一个典型的 Web 桌面应用程序,对于这类型的应用,最大的特点就是很多 RFT 可识别的对象是以树形结构组织并且结构较为固定。同时这些对象是在同一个浏览器窗口内被展现。针对于这样的特点我们可以借鉴树形结构搜索对象的一些基本方法,即从树根开始逐级遍历树的各个节点进行匹配,直到找到我们要操作的 GUI 对象。那么构成这棵树的节点是什么呢?这里我们就要说面一个 HTML 容器对象的概念。
Web 应用说到底是基于 Web 技术,是基于浏览器的。那么不管目前主流的 Web 应用是以何种技术开发,使用的是何种平台,他们都依赖于最基本的 HTML 语言。我们可以发现,HTML 的作用主要是布局 Web 应用的展示层。也就是说 Web 应用的 GUI 布局主要是通过 HTML 的各种标记例如 等来实现的。这些标记划分出的各个区域就像是一个个小的容器,容纳了其他的 GUI 对象。而且这些标记可被 RFT 的对象识别机制识别,对应成相应的 HTML 容器对象。另外还有一个可被 RFT 识别的主要对象,就是 DOM 对象。在 RFT 的对象识别机制中,它是浏览器对象下的第一个容器对象同样对 GUI 元素的识别有重要作用。前面提到的对象树就是由这些容器对象构成的。如下图所示:
(查看图 1 大图)
从上图我们可以看出,容器对象的包含关系是有一定的规则的。也就是说 HTML DOM 是浏览器对象下的第一个对象,承载整个页面的内容。THML DOM 对象之下可以是 Frameset 对象、Frame. 对象、Form. 对象、或者是 GUI 对象。Frameset 对象之下只能是 Frame. 对象,Frame. 对象之下又可以是 HTML DOM 对象或者 Form. 对象。这样由 HTML 容器对象便可以组成容器对象树。而被操作的 GUI 对象元素就是这棵树的叶子节点。从根对象出发找到某个 GUI 对象的过程就是沿着这棵树的某个分支搜索 GUI 对象的过程。如下图:
从图中我们可以看到,从 A 对象开始找到 D 对象的过程经过了 B 和 C 对象。如果 D 对象就是我们要操作的 GUI 对象,那么它上层的对象就是包含它的容器对象。从前面的介绍中我们知道,容器对象在应用中是不可见的。也就是说他们都是些 HTML 标记,在满足 HTML 语法规则的情况下相互嵌套,构成了整个页面的布局。按照 ITCL 框架的规则,在对象层我们要定义 A、B、C 三个容器对象,同时实现 geter 方法。A 对象首先定位到 B 对象,然后 B 对象搜索得到 C 对象。这样逐级搜索得到对象便于对象层的扩展。如果产品修改了 UI 的设计使得 A 对象下面多出了 B' 对象,那么只要在 A 对象内重新实现 B' 对象的 getter 方法。
以 lotusLive Meeting 为例,借助于 RFT 的对象查看器(Object Inspector)我们绘制出了容器对象的基本架构。如下图:
(查看图 3 大图)
从图中我们可以看到,由 HTML 容器节点构成的树将应用的整个页面进行了划分。被操作的 GUI 对象都是树的叶子节点。从图中我们可以看到如果我们想找到并操作位于 ToolbarDOM 对象下的一个 button 对象的话,那么我们要做的就是通过这样一条路径: Root->ConsolDOM->MainFrameSet->ConfwinFrame->ConferenceWindowDOM->VframeSet->ToolbarFrame->ToolbarDOM->Buttom object 。在具体的实现过程中,上图中的所有容器对象都将被定义成一个类。这些类有一个共同的基类那就是 RFT 提供的 TestObject 类。通过对这些容器类的实例化对象调用 RFT 的方法,也就是我们常使用的动态查找对象的方式,即对象的 find() 方法来逐级的找到要操作的 GUI 对象元素。ConsolDOM 类的代码示意如下:
public class ConsolDOM extends HtmlDocument { public ConsolDOM(TestObject arg0) { super(arg0); } public ConsolDOM(TestObjectReference arg0) { super(arg0); } ObjectGetter g = new ObjectGetter(); public MainFrameSet getMainFrameSet() { try {return new MainFrameSet(og.getObject(this, Webfuncs.gsClassProp , Webfuncs.umHFraSetRef, Webfuncs.gsIDProp, "main"));} catch(Exception e){return null;} } } |
从代码中我们看到,除了构造方法 ConsolDOM 类只实现了一个 Getter 方法 getMainFrameSet(),因为它的下层只有这一个容器对象。MainFrameSet 类的实现代码如下:
public class MainFrameSet extends HtmlFrameSet { public MainFrameSet(TestObject arg0) { super(arg0); } public MainFrameSet(TestObjectReference arg0) { super(arg0); } ObjectGetter g = new ObjectGetter(); public ConfwinFrame. getConfwinFrame() { try{return new ConfwinFrame(og.getObject(this, Webfuncs.gsClassProp , Webfuncs.umHtmlFrameRef, Webfuncs.gsIDProp, "confwin"));} catch(Exception e){return null;} } } |
ConferenceWindowDOM 类的实现代码如下:
清单 3. ConferenceWindowDOM 类的实现代码
public class ConferenceWindowDOM extends HtmlDocument { public ConferenceWindowDOM(TestObject arg0) { super(arg0); } public ConferenceWindowDOM(TestObjectReference arg0) { super(arg0); } ObjectGetter g = new ObjectGetter(); public VframeSet getVFrameSet() { try{return new VframeSet(og.getObject(this, Webfuncs.gsClassProp , Webfuncs.umHFraSetRef, Webfuncs.gsIDProp, "vframeset"));} catch(Exception e){return null;} } } |
VframeSet 类的实现代码:
public class VframeSet extends HtmlFrameSet { public VframeSet(TestObject arg0) { super(arg0); } public VframeSet(TestObjectReference arg0) { super(arg0); } ObjectGetter g = new ObjectGetter(); public ToolbarFrame. getToolbarFrame() { try{return new ToolbarFrame(og.getObject(this, Webfuncs.gsClassProp , Webfuncs.umHtmlFrameRef, Webfuncs.gsIDProp, "toolbar"));} catch(Exception e){return null;} } public HframeSet gethFrameSet() { try{return new HframeSet(og.getObject(this, Webfuncs.gsClassProp , Webfuncs.umHFraSetRef, Webfuncs.gsIDProp, "hframeset"));} catch(Exception e){return null;} } public StatusFrame. getStatusFrame() { try{return new StatusFrame(og.getObject(this, Webfuncs.gsClassProp , Webfuncs.umHtmlFrameRef, Webfuncs.gsIDProp, "status"));} catch(Exception e){return null;} } } |
从 lotuslive Meeting 容器架构图中我们看到,VframeSet 容器下有三个 THML 容器 ToolbarFrame、 HframeSet、 StatusFrame。所以要实现着三个容器的 Getter 方法。类似的 ToolbarFrame. 要实现一个 ToolbarDOM 的 Getter 方法,最终得到 ToolbarDOM 对象。以查找 ToolbarDOM 下的一个 button 为例,示例代码如下:
清单 5. 任务层中通过 Root 对象找到 consolDom 对象
TestObject[] to=root.find(atDescendant(Webfuncs.gsClassProp,Webfuncs.gsDocumentRef, Webfuncs.gsTitleProp,VisualReporter.gsUMDomTitle)); if(to.length>1) { for(int i=0;i |
在任务层中由 consolDom 对象得到 VframeSet, 再通过 VframeSet 找到 ToolbarDOM:
MainFrameSet mainFrameSet=consolDom.getMainFrameSet(); ConfwinFrame. confirwinFrame=mainFrameSet.getConfwinFrame(); ConferenceWindowDOM conWinDom=confirwinFrame.getConferenceWindowDom(); VframeSet vframeSet=conWinDom.getVFrameSet(); ToolbarFrame. toolBarFrame=vFrameSet.getToolbarFrame(); ToolbarDOM toolBarDom=toolBarFrame.gettoolbarDom(); |
从上面的代码可以看到,各个对象类都实现了 Get 方法来获取下一级被包含的对象,这样做有几个好处:
- 对于对象层的类来说的查找的范围使得查找更为精确。很大程度避免了同一组查找属性筛选出了多个对象的情况。
- 避免了每次查找对由于各个对象逐,结构清晰,便于维护。
- 对象的查找时逐级进行的,缩小级的调用 find 方法象都从 Root 对象开始查找,提高了查找执行的效率。
即使是使用 RFT 的 API 来动态查找对象,查找也是基于某些对象的识别属性进行的。所以要先通过 RFT 来探测对象的可识别属性。利用 RFT 提供的一个 Tool—对象识别器(Object Inspector)可以方便的查看对象属性。如下图:
图 4. 对象识别器示例(查看图 4 大图)
这里我们选择对象的识别属性的时候有些技巧。我们一般优先选择实现层的属性,尽量避免选择 UI 展示层的属性。例如我们会选择 .id、.classIndex、.classtype、.name、.title 等属性,而尽量不选择 UI 上面的 String 或者是对象的 .text 属性。因为这些 UI 展示层的属性经常会随着产品的版本更替而频繁的发生改变,对自动化脚本的维护带来很大不便。特别是在 GVT 的自动化测试中,经常有需求一套自动化脚本要跑在不同语言的 build 上面。这就需要脚本不依赖于或者尽量少依赖于 UI 的展示层属性,而是在实现层筛选属性来识别对象。这样可以做到一套脚本在不同的语言 UI 上运行,并且实现层的属性较为稳定,在产品进行小版本的升级过程中变化不大,大大的降低的代码的维护成本。另外,当我们使用实现层的属性来查找对象的时候,如果每次都从 Root 对象来查找的话,经常会遇到帅选出多个对象的情况。因为实现层的属性唯一性不够,所以经常查到多个符合要求的对象,例如 OK 按钮之类的对象。这样对精确定位对象带来了麻烦。但是,采用了前面提到的树状结构组织对象,进行自顶向下的逐级查找,这样就可以逐步的缩小查找的范围,做到相对精确的定位 GUI 对象了。
从上文可以看出,利用树状结构来组织 HTML 容器对象,从而划分 GUI 元素的方式可以大大的提高对象层的稳定性。同时降低对象识别属性和 UI 展示层的耦合度,可以方便的使用实现层的属性来识别对象。自顶向下的逐步缩小查找范围可以更精确的定位 GUI 元素的位置。结合 ITCL 框架的使用可以很大程度提高脚本的适应性,稳定性。使自动化脚本的开发适应敏捷(Agile)模式的产品开发特性,以及 FVT、BVT、甚至是 GVT 测试中不断变化的 GUI 设计和实现,降低了脚本后期维护的代价。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14780914/viewspace-628573/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/14780914/viewspace-628573/