RFT的动态特性
动态校验点测试的思想能够被实现的前提是RFT能够提供强有力的支持。而RFT本身一些优秀的特性恰恰证明了这种动态校验的可行性,下面我们通过两方面来展示RFT的相关特性。
首先这种可行性是因为RFT的对象识别体系,它提供了把所有被测试控件映射成为一个树形结构的能力,这使得通过程序来动态遍历所有被测试控件成为可能。因此,测试人员可以不用静态地一个个的抓取和维护被测试控件,而采用配置加遍历的方法来进行动态查找,这就大大减少了代码的耦合性。
在RFT对象识别体系中,每个被测试的对象都被映射成为TestObject的子类实现。这些TestObject通过树形结构组织在一起,用以映射被测试对象中物件相互包含的关系。一个典型的例子如图 4 所示。在这个例子中,整个eclipse的Properties View都映射成为一个TestObject的树形结构。
图 4: 对象映射机制
其次这种可行性来自于RFT的TestObject类提供的丰富的对象属性。正是因为有这些属性的存在,测试人员才可能在动态遍历寻找目标时,精确地判断相对位置关系和文字特征。RFT中每个TestObject对象都可以使用getProperties和getProperty方法来获得主要的属性。在众多的属性值当中,"bounds", "class", "text"是最主要使用到的。"bounds"参数可以返回当前被测试控件的坐标范围,"class"参数返回被测试对象的eclipse类名称。"text"参数返回被测试对象现实出来的文字。
有了如上的思路和RFT特性的支持,在后面的篇幅里面,我们着手分四个步骤来实现这样的动态校验点测试。
首先测试人员需要建立一个标准的RFT测试工程。如图 5 所示,我们建立工程CatProject。
图 5: CatProject工程的目录
然后建立控件定义配置文件\testcases\myvpcase\objects.properties。
NAME=Name:,Right,Text
LABEL=Label:,Right,Text
TYPE=Type:,Right,Text
MANAGEMENT=Management:,Right,Text
EXTENTSIZE=Extent size:,Right,Text
PREFETCHSIZE=Prefetch size:,Right,Text
OVERHEAD=Overhead:,Right,Text
TRANSFERRATE=Transfer rate:,Right,Text
该文件中,每一行都配置了一个被测试的控件。等号左边是这个对象的唯一标识符,该标识符通常可以根据对象的某些特征来命名,比如说控件的文字或者它的前置文本。等号右边是该对象的特征描述,是用来动态识别这个对象的关键。这个特征描述分成三个部分,之间使用逗号进行分隔。这三个部分分别是:相邻的特征标签的文字,被测试控件和特征标签之间的相对位置关系,该被测试控件的类型。比如第5个控件,它左边的标签是EXTENTSIZE:,而这个控件是文本类型的控件,所以可以配置成为EXTENTSIZE=Extent size:,Right,Text。
在完成控件配置文件之后,就可以着手配置预期值。如下所示。(\testcases\myvpcase\ids.properties)
NAME=DB_LARGE1
LABEL=
TYPE=LARGE
MANAGEMENT=DATABASE_MANAGED
EXTENTSIZE=32
PREFETCHSIZE=32
OVERHEAD=12.67
TRANSFERRATE=0.18
在上面的配置文件中,每行都是一个预期值的配置。等号左边是objects.properties中配置的被测试控件的标识符,等号右边是它应当等于的值。通过这种方式,测试人员就可以很容易的配置每个被测试控件的预期值。
在完成配置文件的定义之后,就可以着手进行代码的编制。首先需要定义常数类,存储几个会经常使用到的字符串。这是一个纯Java的接口文件(\library\vputil\IConstant.java)。
package library.vputil;
public interface IConstant {
public static String IDSFILE="ids.properties"; //用来配置预期值文件的名称
public static String OBJECTSFIlE="objects.properties"; //用来配置控件定义文件的名称
public static String LABELCLASS="org.eclipse.swt.custom.CLabel";//标签控件的类名称
public static String LABELCLASS2="org.eclipse.swt.widgets.Label";//标签控件的类名称2
public static String TEXTCLASS="org.eclipse.swt.widgets.Text"; //文本控件的类名称
}
然后,创建一个用来描述对象的对象定义类。它也是一个纯Java的类文件。如\library\vputil\ObjDef.java所示。
package library.vputil;
public class ObjDef{
private String objId; //该控件的唯一标识字符串
private String label; //控件附近的标志性标签文字,比如"Name:"
private String pos; //控件和标签的相对位置关系,比如"Right"和"Left"
private String type; //控件的类型,比如"text"或者"combo"
private String input; //input字段存储该控件的预期值
//以下省略了ObjDef类所有属性的get和set方法
//……
}
另外为了让TestObject类能够承载更多的信息,我们创建一个新类ExtTestObject,并使用组合的模式来扩展TestObject。它内部除了包含一个TestObject的引用之外,还包含两个偏移变量。这两个偏移变量用来记录当前TestObject相对于最原始基准的偏移量。该文件是一个纯Java类。以下是\library\vputil\ExtTestObject.java的源代码。
现在开始创建主要的类PanelVerifier类。PanelVerifier类负责主要的校验流程和关键参数的传入。首先创建PanelVerifier类框架如下:
package library.vputil;
public class PanelVerifier {
}
在类PanelVerifier中添加tabContent变量和dataPath变量,这两个变量需要在初始化的时候传入。tabContent是所在的控件容器的TestObject映射对象,而dataPath则指ids.properties和objects.properties两个文件的路径。
private TestObject tabContent=null; //控件容器的引用
private String dataPath=null; //预期值文件和控件定义文件的存储位置
public PanelVerifier(String dataPath,TestObject tabContent){
this.tabContent=tabContent;
this.dataPath=dataPath;
然后,添加方法readProperties来读取配置文件。readProperties方法读取预期值文件和控件定义配置文件,将这些读取的值封装在一个List中,List的每个元素都是ObjDef的实例。每个ObjDef对应于一个被测试的控件对象,其中中的各种属性值都来自于配置文件。
接下来,为类PanelVerifier增加一个filterSubitems方法,该方法能够在一个被测试对象内部寻找属性符合预期的子对象。比如filterSubitems(tabcontent, "text","Name"),就是在tabcontent的所有子对象中寻找符合属性text等于Name的子对象。
private List filterSubitems(ExtTestObject testobject,
String propertyname,Object propertyvalue){
TestObject[] children =testobject.getTestObject().getChildren();
ArrayList list=new ArrayList();
Rectangle r=(Rectangle)testobject.getTestObject().getProperty("bounds");
for(int i=0;i TestObject subitem=children;
ExtTestObject eto=new ExtTestObject(subitem);
eto.setOffsetX(testobject.getOffsetX()+r.x); //累计x偏移量
eto.setOffsetY(testobject.getOffsetY()+r.y) ; //累计y偏移量
Object value=subitem.getProperty(propertyname);
if(value!=null && value.equals(propertyvalue)){ //判断对象是否符合要求
list.add(eto);
}
list.addAll(filterSubitems(eto,propertyname,propertyvalue));
}
return list;
}
在所有准备工作都完成之后,就可以着手实现verify方法。该方法的主要流程如图6所示。该方法通过遍历控件容器中的所有对象,首先寻找到所有的特征标签。然后根据特征标签的边界和相对位置关系去发现被测试控件,之后就可以根据被测试控件的属性来判断校验点是否符合预期值。Verify方法的源代码可以在附件中找到。
图 6: verify方法的流程
最后,我们生成\testcases\myvpcase\TestCase这个测试脚本,用来最终解答问题2。在它的testMain方法中我们调用PanelVerifier类如下。其中tabContent()是控件容器对应的测试对象。
String dataPath=(String) getOption(IOptionName.DATASTORE); //获取项目所在的路径
dataPath+="\\testcases\\myvpcase"; //dataPath指向具体的配置文件所在路径
PanelVerifier pv=new PanelVerifier(dataPath,tabContent()); //构造PanelVerifier对象
pv.verify(); //执行动态校验
在前面提到,在动态的测试框架下,如果需要进行回归测试,或者增加/减少被测试控件,只需要修改两个配置文件并且重新运行即可。在这个过程中,预期值、测试过程和被测试对象之间的耦合被大量减少,带来的是代码可读性和可维护性的提高。然而,该方法也是以损失一部分性能为代价的,因为对象的动态识别需要至少两次遍历tabContent内部的所有对象,这就损失了一些效率。幸运的是,对于大多数基于eclipse的被测试程序,根据实践经验用户通常感觉不到这方面性能的问题。
本文提供的源程序是在基于eclipse的被测试程序上运行和分析的结果,而且相对关系的处理仅限于右相对关系,可识别的控件只支持文本控件。对于非基于eclipse的被测试对象,比如html或者普通java程序,被识别控件的类型是很丰富的,但只要遵循相同的思路,通过增加相应的代码就能够实现更多的自动识别的功能。
动态的校验点测试的另外一个巨大的潜力在于,它有潜力发展成为一种校验点测试的框架。测试人员使用自动化测试工具来进行测试,其本质目的是为了能够减少手工重复点击和校验,所以测试人员书写的代码脚本越少越少。将测试人员关注的业务部分单独配置出来,而将他们不熟悉的部分封装在一个框架中,会大大提高测试工作的有效性。而本文提供的动态校验点测试正是在这个方面做出了一定的探索。在这个基础上,再增加配置文件的管理、多种控件/相对位置的识别,再辅之于输入输出的接口,它就可能是一个通用性很强的RFT测试框架。
RFT自身强大的特性决定了测试人员可以通过解耦提高测试代码的可用性,减少维护成本。Java语言的支持使得RFT能够在运行时处理更多的问题,同时能够将Java世界中许多优秀的框架和体系引入到自动化测试的领域中来。