对 TestObject.find() 方法的扩展与改进

在使用 IBM Rational Functional Tester(RFT)进行软件测试的时候,为了减少对象层次结构发生变化带来的脚本维护的成本,通常我们会使用动态识别而不是静态识别机制。所谓动态识别,一般会使用 TestObject.find() 方法查找对象,而不去关心对象的层次结构。RFT 对于对象层次结构相对复杂,或者编码不是很规范的控件的识别,尤其基于 Windows 开发的产品,在使用动态查找方法时会遇到一些困难,甚至有无法正确获取对象的情况。本文将结合实际应用 TestObject.find() 方法遇到的问题,分析并给出解决方案.

理解并使用 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
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); 

使用 TestObject.find() 方法时遇到的问题

在 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

通过上面的 ObjectMap 图,我们看到使用 .NET 开发的控件,被 RFT 识别为 win domain 而不是 Net domain,而且对象结构又比较复杂,这可能是造成 atDescendant() 方法无法识别对象的原因。

可见,RFT 的 TestObject.find() 提供的 atDescendant() 方法对于处理复杂的,编写不规范的对象控件,有其不足。对于这个问题,在此我们不做过多讨论。我们所关心的是如何利用现有的技术和 RFT 提供的可用的方法,来解决这个问题,从而顺利的查找到我们需要的对象。


如何解决

正如上面所提到的,RFT 的动态识别方法,在解决复杂结构尤其是 windows 控件的查找时会出现一些问题,所以我们尝试用其他方法来查找对像,在实践中我们发现 TestObject.getChildren() 方法总是可用,不会出现查找超时而无法找到对象的情况,而且效率很好,所以如果我们利用 getChildren() 递归查找所有对象的各层子对象,并利用 TestObject.getProperty() 方法获取所需对象属性,进行比较筛选,这样,只要我们提供的属性能唯一标识该对象,利用递归的方法逐一进行查找比较,我们就能顺利找到这个对象。

下面是递归结构图:


图3. 递归结构图
递归结构图

下面给出一个简单的实现递归查找对象的例子(实例 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 对象的方法来提高查询效率,这里不再赘述。

TestObject.find() 方法结合

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/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值