级别: 初级

黄 腾龙, 软件工程师, IBM

2008 年 6 月 30 日

当使用 Rational Functional Tester (RFT) 进行 Web 应用程序自动化测试的时候,通常会遇到这样一个技术问题。一个请求被提交之后,如何在 Rational Functional Tester 中判断响应页面是否为我们所期待的内容,并且该页面是否已经下载完毕。本文针对这个问题,给出了几个解决办法。

IBM Rational Functional Tester(简称RFT)是一款先进的、自动化的功能和回归测试工具,它适用于测试人员和 GUI 开发人员。使用它,测试新手可以简化复杂的测试任务,很快上手;测试专家能够通过选择工业标准化的脚本语言,实现各种高级定制功能。

Rational Functional Tester对基于Web的应用程序的测试提供了很好的支持,包括其提供的丰富的类库及对不同浏览器的兼容。使得我们在进行Web应用自动化测试的时候,可以快速的开发出可扩展的,能被不同浏览器兼容的自动化测试程序。本文将讨论在用Rational Functional Tester进行Web 应用测试的时候,如何等待页面加载这个问题。

RFT 中如何进行 Web 应用的测试

Rational Functional Tester (FRT) 是一个面向对象的自动测试工具,它可以使您测试多种应用程序。您可以通过记录对应用程序的测试来快速地生成脚本,并且您可以测试应用程序中的任意对象,包括对象的属性和数据。

RFT 会把被测应用程序(Application Under Test,AUT)中所有的元素都看成对象 TestObject, 每个对象都由两部分组成:

  1. 一系列代表对象属性的键值对
  2. 对象的层次结构。

下图展示了在 RFT 中一个普通html页面的视图:
图 1. RFT 中一个普通 html 页面的视图
RFT 中一个普通 html 页面的视图

要想判断一个页面是否全部下载完毕,同样需要满足两点:指定对象要存在,以及其层次结构是否完整。下面我们探讨一下在 RFT 中具体如何实现。

方法一、利用 Object Map

Object map 用于存储被测应用程序(application under test,AUT)中对象,包括对象的属性和层次结构。我们拿 google 的首页作为例子,看下在 RFT 中是如何存储的。我们将把页面中的文本输入框抓取到 RFT 中。


图 2. 以 google 的首页作为例子,看在 RFT 中是如何存储
以 google 的首页作为例子,看在 RFT 中是如何存储

下图为其在 RFT 中的存储形式。由两部分组成:图上部的树型结构,图下部的属性列表。


图 3. 在 RFT 中的存储形式:图上部的树型结构,图下部的属性列表。
在 RFT 中的存储形式

Object map 中被映射的对象使用被存储的、静态的、识别属性和对象层次来识别,因此利用 Object Map 中存储的对象将能很好的判断对象所在页面是否完整。

利用 RFT 录制功能,将响应页面的一个对象添加到 Object Map,RFT 会自动为该对象生成查找方法。例如,我们可以针对具体页面上(能明显区别开响应页面与其他页面的)的特定页面元素进行识别。在实际测试运行当中,如果该对象能被找到,即证明该页面已经加载完毕。例如,针对页面中的一个按钮 button_XXXbutton(),其返回值为 GuiTestObject 对象,利用 GuiTestObject 的 waitForExistence() 方法会阻塞当前脚本的执行,直到该对象被找到。

RFT 针对该方法提供了两种接口:

button_XXXbutton().waitForExistence(); 和 button_XXXbutton().waitForExistence(double max_time, double wait_time);

这两个接口的区别在于 .waitForExistence() 利用在 RFT 中设置的时间来执行,而 waitForExistence(double max_time, double wait_time); 利用在运行时指定的时间来执行。

1, button_XXXbutton().waitForExistence();

该方法的具体阻塞时间可以通过在 RFT 中设定的一个全局变量来控制。步骤为:选择菜单 WindowPreferencesFunctional TestPlay back 中设置 Maximum time to attempt to find Test Object 的值,其单位为秒,如图所示。脚本中所有 waitForExistence()方法将默认阻塞所设时间。


图 4. 默认阻塞所设时间
默认阻塞所设时间

2,button_XXXbutton().waitForExistence(180, 1);

通过该方法可以在运行时动态设置阻塞时间,并将覆盖默认值。

采用 Object Map 是 RFT 最基本,最全面支持的方法,它和 RFT 强大的录制回放功能进行了无缝的集成,下面给出了他的一些优缺点。

优点:利用 Object Map 来查找对象,RFT 将完全匹配对象的属性和树状结构,所以该方法能很准确的匹配到相应页面元素,同时也能很准确的判断整个页面是否下载完毕。

缺点:该方法由于依赖于 Object Map, 向 Object Map 中录入对象需要手工来实现,而且针对不同的页面都需要重新录制一个对象,所以不可避免的存在静态识别的重用性问题。对于每个不同的页面,我们都要抓取一个对象到 Object Map 中,这样在不同页面中很难做到重用,而且如果页面结构发生变化还会带来维护成本高的问题。

方法二、利用 TestObject 的 find 方法

如果想避免录制动作以向对象地图(Object Map)中添加 TestObject 对象,我们可以采用 TestObject.find 方法 TestObject.find 方法是一个 Java™方法, RFT 使用它在运行时动态地在被测应用程序(Application Under Test)中定位 TestObject,其根据对象的属性在运行时进行识别,可由脚本编写人员在运行时动态调整对象识别属性,因此其带来了极大的灵活性。由于其未在 Object Map 中录制任何对象,所以不能调用 waitForExistence() 方法来阻塞等待,我们可通过一个循环来实现,示例代码如下:

while ( 等待时间值 ){
            if( find(atDescendant(".class", "Html.Button") != null){
            return;
            }
            sleep(1);
            }
            

该方法将不断识别指定对象 ( 能明显区别开响应页面与其他页面的一个对象 ),直到识别成功或时间超时。

TestObject.find 方法是 RFT 提供给脚本开发人员的一个强大的接口,在开发可维护的脚本过程中不可避免的会用到该方法,而且其执行效率也得到了很大改进,下面给出了利用它在等待页面下载时的一些优缺点。

优点:利用该方法我们将能充分利用 find 方法的动态性和可重用性。

缺点:由于 find 方法只能依赖于对象的属性,而不会依赖于对象的层次结构来进行识别,所以即使我们找到了对象,也不能判断当前页面是否下载完毕。比如,页面中的图片资源会在其他元素下载完毕后再下载,所以如果此时对这些图片进行操作的话将会出现异常。


方法三、利用浏览器对象的 readyState 状态

除了上述两种方法,如果当前之存在一个浏览器窗口,则可以利用浏览器对象的 .readyState 状态来进行判断。

如果响应页面中某个对象能被 RFT 识别(即该对象已下载成功)则该对象的 readyState 属性值将会是”4”或”complete”。不同浏览器中 readyState 的值会不一样,IE 中为 complete, FireFox 中为 4。(RFT 从 7.0 版本开始对 IE 及 FireFox 全部支持 )。

上面提到过 RFT 以树型结构的方式来识别对象,越外层的对象所包含的页面元素越多,因此我们可以利用浏览器对象的 readyState 值是否为”4”或”complete”来判断当前页面是否下载完毕。如果浏览器对象已下载成功,则页面其他元素(即其所有子元素都已下载),或者可将 HtmlBrowser 对象录入到 Object Map 中并调用 HtmlBrowser.waitForExistence() 方法 ( 不同的 HtmlBrowser 在 RFT 中并没有区别,所以将其录入到 Object Map 中可以达到重用的目的 ),可达到与上述方法同样的效果。

while ( 等待时间值 ){
            if( find(atDescendant(".class","Html.Browser").
            getProperty(“readyState”).equals(“complete”)){
            return;
            }
            sleep(1);
            }
            

该方法利用 Test Object 的一个属性值来进行判断,在只存在一个浏览器对象的时候这个方法能很好的帮我们解决页面等待问题。

下面对他的优缺点进行下比较。

优点:可准确的判断页面是否下载完毕,并且很容易做到重用。

缺点:由于该方法依赖于浏览器对象,而不能匹配页面具体元素,所以很难做到唯一指定一个页面。比如同时打开了多个浏览器页面,或者在请求页面和响应页面之间有个等待页面,这些情况下该方法不能很好的工作。


方法四:结合方法二和方法三

如前所述,方法一存在重用性问题,方法二不能准确判断页面是否下载完毕,而方法三在同时打开多个浏览器页面时不能准确识别,因此单独采用上述三种方法都不能很好地完成任务,不过我们可以结合方法二和方法三利用方法二中的 find 方法,可以指定响应页面中的一个标识对象,这样即使有多个页面也能唯一确认响应页面。同时利用方法三中浏览器对象的 readyState 状态值来判断当前页面是否下载完毕,这样的结合就能达到准确判断响应页面是否下载完毕的目的。

while(true){
            if(getButton("Finish") != null && new BrowserTestObject(getButton("Finish").
            getTopParent().getChildren()[0]).
            getProperty("readyState").equals("complete")){
            return;
            }
            sleep(1);
            }
            

在方法三种我们提到可用 Object Map 中录入 HtmlBrowser 对象的方法来替代判断浏览器对象 readyState 状态的方法,但如果同时打开多个浏览器页面,将无法区分开来。所以此处最好采用上述代码中所列方法:判断当前对象所在的浏览器的 readyState 状态。

由于该方法完全采用了动态识别的方式,因此我们在程序中可以更好的来重用它,通过定义一个 Helper super class,在这个类中定义上述方法,需要调用该功能的类通过继承这个 class 并将需要动态查找的对象作为参数传递给该 class 这种方式,我们能在代码级得到更好的重用。

优点:该方法能准确的找到指定页面并且能很好的判断页面是否下载完毕。而且利用了 find 方法来动态识别能很好的做到重用。

五、总结

自动化测试的页面等待是一个常见的问题,本文介绍了 RFT 中常用的几种解决办法。每一种方法都有自己的优缺点,这些方法的差别也正体现了 RFT 中静态与动态识别的差异,在具体的实践过程中我们可以依据具体的应用类别来选择合适的解决方案。

例如:在相似页面重复很少的情况下,我们可以采用方法一,这样可以充分利用 Object Map 的易用性及性能优势在相似页面重复较多,同时打开多个浏览器页面,页面响应比较快的时候可以采用方法二在只打开一个浏览器页面的时候可以采用方法三在相似页面重复较多,同时打开多个浏览器页面,而且页面响应比较慢的时候可以采用方法四。



参考资料

学习

获得产品和技术

讨论
  • 参加 Rational 大学,与 IBM Rational 专家一起分享 Rational 产品最佳实践。

关于作者

黄腾龙,软件工程师,目前在 IBM 中国软件开发中心工作。对 J2EE 应用程序开发,软件过程及自动化测试有着浓厚的兴趣。