在使用 IBM Rational Functional Tester(RFT)进行软件测试的时候,为了减少对象层次结构发生变化带来的脚本维护的成本,通常我们会使用动态识别而不是静态识别机制。所谓动态识别,一般会使用 TestObject.find() 方法查找对象,而不去关心对象的层次结构。RFT 对于对象层次结构相对复杂,或者编码不是很规范的控件的识别,尤其基于 Windows 开发的产品,在使用动态查找方法时会遇到一些困难,甚至有无法正确获取对象的情况。本文将结合实际应用 TestObject.find() 方法遇到的问题,分析并给出解决方案.
在 RFT 提供的 API 包中 TestObject.find() 方法功能强大,使用灵活。通常情况下,只需要提供对象的属性值,并保证属性的唯一性就可以查找所需的对象。
TestObject.find() 方法提供了以下几种使用方式:
- atProperty -- A name/value pair representing a TestObject property
- atChild -- One or more properties that must be matched against the direct child of the starting TestObject
- atDescendant -- One or more properties that can be matched against any child of the starting TestObject
- atList -- A sequential list of properties to match against. Valid subitems for atList are:
- atChild
- atDescendant
- atProperty
下面是一个简单的使用 TestObject.find() 方法查找 button 控件的例子:
首先通过 RFT 自带的 Object Map tool 获取所要查找的控件的可用属性值,以便我们清楚的了解使用 TestObject.find() 方法查找时需要提供哪些属性。
下图是一个 button 控件在 object map 中的表现形式:
图 1. button 控件在 object map 中的表现形式 object map
下面是具体的实例(实例 1):
代码 1. 使用 TestObject.find() 方法查找 button 控件的例子
/** * 通过属性”.class”、”.name”和”.text”查找 button 控件对象 * @param nameValue button 的 .name 属性值 * @param textValue button 的 .text 属性值 * @param object button 的父对象 * @return TestObject */ public TestObject getButtonTestObject(Object nameValue,Object textValue, TestObject object){ TestObject[] tos = null; tos = object.find(atList(atDescendant(".class",”.Pushbutton”,".name",nameValue), atProperty(".text",textValue))); if(tos.length >0 ) { return tos[0]; } } |
例如我们要查找到一个叫”Log in”的按钮,现在我们可以调用上面的方法:
getButtonTestObject(“Log in”,”Log in”,parentObj); |
在 RFT 的 script. 中,我们经常使用的就是 TestObject.find() 方法的 atDescendant() 方式,它可以不用关注对象层次结构,直接对根对象的深层次子对象进行查找,但对于具有复杂的层次结构,编码又不是很标准的控件对象,尤其是基于 windows 开发的应用程序,使用 atDesendant() 经常会遇到查找超时或者根本无法查找到对象的问题。
例如测试一个基于 Office2007 开发的 Add-In 产品,开发语言为 .NET framework,如果要在一个 search 面板上查找一个 Search button,通过下面的 ObjectMap 图我们可以看到,它的层次关系十分复杂,search button 是作为顶层的 Search pane 的多级子孙对象存在的。虽然这个 button 控件具有唯一的属性值,但我们尝试用 TestObject.find() 方法的各种方式去查找,返回值都为空,无法找到该对象。
图 2. .NET 开发的控件,被 RFT 识别为 win domain 而不是 Net domain
![.NET 开发的控件,被 RFT 识别为 Windows domain 而不是 Net domain](https://i-blog.csdnimg.cn/blog_migrate/faa698eba7ede680f47f75af1d29d3e3.png)
通过上面的 ObjectMap 图,我们看到使用 .NET 开发的控件,被 RFT 识别为 win domain 而不是 Net domain,而且对象结构又比较复杂,这可能是造成 atDescendant() 方法无法识别对象的原因。
可见,RFT 的 TestObject.find() 提供的 atDescendant() 方法对于处理复杂的,编写不规范的对象控件,有其不足。对于这个问题,在此我们不做过多讨论。我们所关心的是如何利用现有的技术和 RFT 提供的可用的方法,来解决这个问题,从而顺利的查找到我们需要的对象。
正如上面所提到的,RFT 的动态识别方法,在解决复杂结构尤其是 windows 控件的查找时会出现一些问题,所以我们尝试用其他方法来查找对像,在实践中我们发现 TestObject.getChildren() 方法总是可用,不会出现查找超时而无法找到对象的情况,而且效率很好,所以如果我们利用 getChildren() 递归查找所有对象的各层子对象,并利用 TestObject.getProperty() 方法获取所需对象属性,进行比较筛选,这样,只要我们提供的属性能唯一标识该对象,利用递归的方法逐一进行查找比较,我们就能顺利找到这个对象。
下面是递归结构图:
图3. 递归结构图
![递归结构图](http://www.ibm.com/developerworks/cn/rational/r-cn-rftwindows4/image003.jpg)
下面给出一个简单的实现递归查找对象的例子(实例 2):
代码 2. 实现递归查找对象的例子
/** * 递归遍历查找父对象的直接子对象,比较对象的属性值,如果找到则返回,若没有符合的条件的对象, * 则进入二级子对象进行查找,依此类推,通过遍历查找所需对象。 * 例如,在 Search 面板中查找一个叫 search 的按钮,可以调用如下的方法: * getObjectByProperty(".class",".Pushbuttn",".name","Search",searchPaneObject); * @param pro1, 欲查找控件对象的第一个属性,例如 ".class" * @param value1, 欲查找控件对象的第一个属性的值,例如 ".Pushbutton" * @param pro2, 欲查找控件对象的第二个属性,例如 ".name" * @param value2, 欲查找控件对象的第二个属性的值,例如 "Search" * @param testObj, 包含这个控件对象的父对象 * @return TestObject 返回找到的控件对象 */ public TestObject getObjectByProperty(String pro1,Object value1,String pro2, Object value2,TestObject testObj){ // 定义子对象的层次 int level = 0; TestObject[] to1 = null; // 获取父对象的所有直接子对象 TestObject[] to = testObj.getChildren(); // 比较 TestObject[] 的长度是否大于 0,其中 isExist 是一个全局标识,用来判断对象是否找到 if (to.length > 0 && !isExist){ // 循环查找直接子对象 for (int i = 0; i < to.length; i++) { String clsName = ""; String value = ""; // 根据属性名,获取对象的第一个属性值 Object className = to[i].getProperty(pro1); // 根据属性名,获取对象的第二个属性值 Object valueName = to[i].getProperty(pro2); if(className!=null){ clsName = className.toString(); } if(valueName!=null) { value = valueName.toString(); } to1 = to[i].getChildren(); // 如果找到对象则跳出,如果没找到,则递归继续查找 if (clsName.contains(value1.toString())&& value.equals(value2)){ testObject = to[i]; isExist = true; break; } else if ( to1.length >0 ) { level = 0; getObjectByProperty(pro1,value1,pro2,value2,to[i]); } else { level++; } if(level == to.length){ return testObject; } } } else { return testObject; } return testObject; } |
这个例子中,我们提供了两个属性值对,对所需对象进行匹配查找,通常情况下,这个两个属性是 .class 和 .name,当然,如果两个属性并不能满足唯一确定一个对象,你还可以扩展到三个或者多个属性作为函数的参数,从而实现准确查找匹配的对象,这里不再赘述。
虽然随着现在电脑的运算能力的提高,使得循环和递归查询效率在很大程度上提高了,但对于层次结构很深的对象的查询,简单的递归在效率上还需要做适当的优化。
针对我们测试的不同产品,我们可以定义不同的规则来提高查询效率,例如我们要查找一个叫 Search 的 button,它不可能作为 scrollbar 的子对象,所以我们可以在查找 button 时可以跳过对 scrollbar 的查找,从而提高效率。通过实践和总结,我们可以定义一些常用查找规则,在函数中调用这些规则,提高查找对象的效率。
另外,我们还可以通过缩小所需查询的对象的相对父对象的层次结构,并利用 cache 对象的方法来提高查询效率,这里不再赘述。
RFT 提供的 TestObject.find() 方法也有它的优点,我们并不想完全弃用,只是针对有些 TestObject.find() 方法无法找到的对象,结合以上方法,将大大提高对象的查找力度,既保证查找效率,又保证查找的准确性,从而更好的为产品的 automation 测试服务。
下面是一个简单的结合 TestObject.find() 方法的示例:
代码 3. 简单的结合 TestObject.find() 方法的示例
// 结合 RFT framework 提供的 find 方法(实例 1), // 和本文介绍的通过属性 - 值递归查找子对象的方法(实例 2), // 我们就可以大大提高对所需对象的动态查找的准确率。 public TestObject findTestObject(String pro1,Object value1,String pro2, Object value2,TestObject object){ TestObject testObj = null; // 调用实例 1 testObj = getButtonTestObject(pro1,value1,pro2,value2,object); // 如果没有找到,则调用实例 2 if(testObj==null) { getTestObjectByProperty(pro1,value1,pro2,value2,object); } } |
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14780873/viewspace-582399/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/14780873/viewspace-582399/