RFT静态方法转动态方法.docx
- 文档编号:2194887
- 上传时间:2023-05-02
- 格式:DOCX
- 页数:20
- 大小:172.66KB
RFT静态方法转动态方法.docx
《RFT静态方法转动态方法.docx》由会员分享,可在线阅读,更多相关《RFT静态方法转动态方法.docx(20页珍藏版)》请在冰点文库上搜索。
RFT静态方法转动态方法
在RationalFunctionalTester脚本中实现静态获取方法到动态获取方法的自动转换
本文讨论了如何自动地将RationalFunctionalTester(RFT)脚本中的静态获取方法转化为动态获取方法。
您将了解到什么是RFT脚本的静态获取方法和动态获取方法,用动态获取方法有哪些优势,以及怎样自动地将静态获取方法转化为动态获取方法。
背景
随着测试技术的发展,自动化测试越来越受到人们的关注。
RationalFunctionalTester(RFT)就是基于应用程序图形界面(GUI)的自动化测试工具之一。
RFT是基于java语言的测试工具,通过匹配对象属性来识别对象化的UI控件,进而操作这些UI元素完成一系列的事件和流程,实现自动化测试的功能。
一般情况下,在开始一个基于RFT的自动化测试项目的时候,都会选择静态的方法抓取对象,然后再对这些对象进行相应的操作,但是这种做法会给回归测试带来一些不便。
当应用程序的UI有所改变时,静态的获取UI对象可能会变的比较困难。
测试人员不得不重新抓取相应的对象。
静态方法的这种不足正是我们提倡动态方法的意义所在。
静态获取方法
什么是静态获取方法?
静态获取方法是通过RFT提供的测试对象录制(抓取)功能得到UI对象,并在测试脚本的运行中利用UI对象的所有属性来匹配UI对象的方法。
静态获取的方法有简捷、快速的特点,但是当应用程序的UI发生变化后,它的识别能力就会减弱。
这是因为RFT的这种静态获取对象的方法是通过匹配UI对象的所有属性来实现的,它可能造成这样的状况:
UI对象的位置、大小等次要参数如果发生重大变化,就可能导致匹配过程失败。
图1.静态获取方法和测试对象地图(TestObjectMap)
动态获取方法
与静态方法不同,动态获取方法则通过匹配对象的关键属性(通常选择有意义的,不易变化的属性)来获取对象。
这样对于软件的回归测试来说,只要这个对象的这个关键属性没有变化,我们的脚本就不需要重新校正。
这就使得UI对象的长宽、坐标等次要的属性信息不被用于匹配对象,当这些易变的、次要的属性变化的时候,脚本还可以照常工作。
因此动态获取方法在伸缩性、健壮性等方面,有着静态方法无法克服的优势,这将大大减少开发人员维护测试脚本的工作量。
当测试下一个版本的时候,很少受到GUI界面变化的影响。
下面的代码示例了一个动态获取方法:
publicSWTTextgetTxtFirstName_Org(){
GuiTestObjectgto=(GuiTestObject)ObjectFactory.findTestObject("Firstname",
".priorLabel","org.eclipse.swt.widgets.Text",getTabDarOrderEditor());
if(gto==null)returnnull;
returnnewSWTText(gto);
}
在上面的代码中,我们用ObjectFactory的findTestobject()方法来实现动态获取对象。
该方法的四个参数分别是:
用来辨识对象的属性值、属性名称、对象的类型以及对象的父对象。
该方法会把这四个参数作为获取对象的查找标准,与被测试UI中的对象进行匹配。
在上面的代码中,我们试图匹配一个文本输入框,它的其中一个属性名称为“.priorLabel”,也就是这个文本框的名称,属性值为“Firstname”,classId为“org.eclipse.swt.widgets.Text”,说明它是一个文本框,最后的getTabDarOrderEditor()方法将返回一个对象,代表文本框的父对象。
带着这些参数运行的findTestobject()方法将会匹配这样的对象:
一个名称为Firstname,父对象是getTabDarOrderEditor()方法返回的对象的文本框。
如果找到则返回一个SWTText对象,否则返回null。
注:
本文的实现基于IBM的另一个工具,该工具提供了重新封装了RFT定义的各种对象,提供了一些诸如ObjectFactory类的工具,为动态获取对象的实现提供了很多帮助。
读者可以从本文的参考资料部分得到该工具的介绍和源代码。
回页首
为什么静态获取方法要转换成动态获取方法
一般来说在新开发的测试脚本项目中,都会首先采用静态的方法获取对象。
这是因为静态的方法简便、快捷。
但随着不断有新的版本的推出,静态获取方法需要被转化成动态获取方法的需求会越来越明显。
这是由这两种获取方法的特点决定的。
但是如果用手工去做相应的转化,不仅费时费力,而且重复性的劳动会让测试人员身心俱疲。
所以,能够自动完成此项功能的解决方案就显得尤为重要。
回页首
怎样将静态获取方法转换成动态获取方法
将静态脚本转化成动态脚本可以通过编程来实现。
一般的过程是分析测试脚本对象,提取脚本中的静态对象,根据对象的mapId,取得其它一些动态方法需要的参数,比如识别类名和测试对象名,然后拼接字符串而生成动态获取方法。
图2展示了转换静态方法和运行动态方法的过程:
图2.从静态方法到动态方法
回页首
一个自动将静态脚本转化成动态脚本的工具
为了将静态脚本自动转化成动态脚本,我们开发了一个工具来完成这项工作。
该工具主要实现三项功能:
1.根据脚本创建和更新配置文件,把用来查找对象的属性名及属性值抽取到了配置文件中。
这样,即使以后某些对象的属性发生了变化,也可以通过修改配置文件来适应这些变化,而不必修改脚本本身。
2.将静态脚本转变为动态脚本。
3.从配置文件中查找和返回对象
下图是该工具的类图示例:
图3.工具的类图示例
这个工具工作的具体过程包括以下几个步骤:
1)构建配置文件。
首先获取测试脚本中所有的UI对象,遍历这些UI对象,取得UI对象的mapId,再据此得到其识别类名、Role以及测试对象名,再据此得到UI对象的可供动态查找使用的属性名和属性值,如果无法得到合适的动态属性,则将这个对象的查找方法设置为静态查找,最后将这些信息保存到一个配置文件中。
下图展示了该步骤的具体流程:
图4.持久化对象的属性到配置文件
值得注意的是,我们约定每个脚本文件(代表一个RationalTestScript对象)包含某个界面里的所有对象,为了便于统一处理,这个界面的最外层对象需要命名为“TopContainer”。
下面是一些关键步骤的代码演示:
a.
获取脚本中所有UI对象的名称。
IScriptDefinitionsd=TS.getScriptDefinition();
Enumeratione=sd.getTestObjectNames();
b.
创建一个Document对象来保存脚本中所有对象的信息,并将根元素(或节点)设为TopContainer。
然后获取TopContainer对象的mapID,并默认的设置TopContainer的查找方式为静态,如果TopContainer对象能够动态获得,则设置查找方式为动态。
接着获取必要的对象属性信息并保存到Document对象的元素(element)属性(attribute)中。
最后再将该元素添加到Document对象中。
下图展示了获取对象属性之一className的调用过程:
图5.获取对象属性的次序图
(查看大图)
下面是关键步骤的代码演示:
//新建一个Document对象,并将根元素(或节点)设为TopContainer。
DocumentBuilderFactorydomfac=DocumentBuilderFactory.newInstance();
DocumentBuilderdomBuilder=domfac.newDocumentBuilder();
Documentconfig=domBuilder.newDocument();
Elementroot=config.createElement("TopContainer");
//获取TopContainer的mapID
StringmapID=sd.getMapId("TopContainer");
//默认TopContainer的查找方式为静态
root.setAttribute("isStatic","true");
//获取必要的信息并保存到元素属性中
root.setAttribute("class",TS.getMap().find(mapID).getClassName()
.toString());
root.setAttribute("role",sd.getRole("TopContainer").toString());
root.setAttribute("objClass",TS.getMap().find(mapID)
.getTestObjectClassName().toString());
root.setAttribute("propName","mappedName");
root.setAttribute("propValue","TopContainer");
root.setAttribute("parent","self");
//将元素添加到Document对象中
config.appendChild(root);
c.
遍历所有对象,将需要的对象属性保存到Document对象中。
该步的处理方式与上一步相似,区别仅在于需要遍历的处理所有非TopContainer的对象。
while(e.hasMoreElements()){
StringobjName=e.nextElement().toString();
//如果遍历到TopContainer,略过
if(objName.equalsIgnoreCase("TopContainer"))
continue;
//获取当前对象的mapID
mapID=sd.getMapId(objName);
//获取当前对象的类名
StringclassName=TS.getMap().find(mapID).getClassName().toString();
//获取当前对象的对象类名
StringobjClassName=TS.getMap().find(mapID)
.getTestObjectClassName().toString();
//获取当前对象的Role
StringobjRole=TS.getMap().find(mapID).getRole();
根据当前对象的类名、对象类名以及Role得到可作为动态查找的属性名
StringpropName=getDftPropForIdentify(className,objRole,
objClassName);
StringisStatic="false";
//如果没找到匹配的属性,将此对象的查找方法设为静态
if(propName==null||propName==""){
isStatic="true";
Elementnode=config.createElement(objName);
node.setAttribute("isStatic",isStatic);
node.setAttribute("class",className);
node.setAttribute("role",objRole);
node.setAttribute("objClass",objClassName);
node.setAttribute("propName","mappedName");
node.setAttribute("propValue",objName);
node.setAttribute("parent","TopContainer");
root.appendChild(node);
}else{
//得到可作为动态查找的所有属性
ArrayListpropNames=getPropNameList(propName,"#");
StringpropNameToId=null;
StringpropValue=null;
booleanisFoundProp=false;
//遍历所有属性,得到当前对象具有的属性
for(inti=0;i isFoundProp;i++){ propNameToId=(String)propNames.get(i); try{ propValue=TS.getMap().find(mapID).getProperty(propNameToId).toString(); }catch(Exceptionex){ //当前对象不具备此属性,不做处理,继续尝试其它属性 } //找到属性,退出循环 if(propValue! =null&&propValue! ="") isFoundProp=true; } //如果属性值为空,将对象查找方法设为静态 if(propValue==null||propValue=="") isStatic="true"; //将对象信息保存到XML对象 Elementnode=config.createElement(objName); node.setAttribute("isStatic",isStatic); node.setAttribute("class",className); node.setAttribute("role",objRole); node.setAttribute("objClass",objClassName); node.setAttribute("propName",propNameToId); node.setAttribute("propValue",propValue); //默认的父对象为TopContainer node.setAttribute("parent","TopContainer"); root.appendChild(node); } } d. 持久化Document对象到XML文件中。 当运行测试脚本时,将从此配置文件中读取对象属性去匹配UI上的对象。 下面是一个配置文件的例子。 图6.配置文件的结构 2)将静态脚本转变为动态脚本,这是实现动态获取UI对象的关键步骤。 我们已经通过上面的步骤将脚本中所有对象的属性以XML格式保存在对象属性配置文件中,利用工具中的getObj()方法,就可以在脚本运行时,通过对比配置文件中的对象属性,定位到被测试UI中的对象。 所以这一步的主要工作就是把原来脚本中获取对象的方法用工具提供的getObj()方法替换。 为了避免手工替换脚本的巨大人力开销,我们提供了updateDynamicObjMethod()方法来实现脚本的自动转变。 在这个方法中,我们将动态脚本分成三部分,头部、主体和尾部。 其中,头部包括包声明、包引入、类声明以及读取配置文件中的信息;主体包含了所有的获取UI对象方法;尾部则在脚本文件共有的testMain方法中提供了一段代码,用以测试每一个获取对象的方法是否工作正常,下面分别展示了动态脚本三个部分的代码片断: 头部: packageappObjects.logon; importibm.widgets.WButton; importibm.widgets.WFrame; importibm.widgets.swt.SWTText; importibm.widgets.swt.SWTTree; importjava.io.File; importjava.lang.reflect.Method; importjava.util.ArrayList; importresources.appObjects.logon.SC_DemoHelper; importappObjects.ObjConfig; importcom.rational.test.ft.object.interfaces.TestObject; publicclassSC_DemoextendsSC_DemoHelper{ privatestaticObjConfigconfig=null; publicObjConfiggetObjConfig(){ returnconfig; } publicSC_Demo()throwsException{ if(config==null){ try{ StringconfigFileFullName=this.getClass().getName(); StringconfigFileName=configFileFullName.substring( configFileFullName.lastIndexOf(".") +1)+".xml"; config=newObjConfig(this.getClass().getResource(".." +File.separator+"config"+File.separator +configFileName).getPath()); }catch(Exceptione){ e.printStackTrace(); throwe; } } } 尾部: publicvoidtestMain(Object[]args)throwsException { Method[]methods=this.getClass().getDeclaredMethods(); ArrayListfailedMethods=newArrayList(); for(inti=0;i System.out.println("processing: "+methods[i].getName()); TestObjectto=null; if(methods[i].getName().equalsIgnoreCase("getObjConfig")|| methods[i].getName().equalsIgnoreCase("testMain"))continue; try{ to=(TestObject)methods[i].invoke(this,null); }catch(Exceptione){ e.printStackTrace(); failedMethods.add(methods[i].getName()); continue; } if(to==null){ failedMethods.add(methods[i].getName()); } } if(failedMethods.size()==0){ System.out.println( "Congratulations! Everymethodworkswell! " ); }else{ System.out.println( "Thefollowingmethodscan'twork,pleasecheckthemmanually: " ); for(inti=0;i System.out.println(failedMethods.get(i)); } } } } 一般的,对于每一个动态脚本来说,头部和尾部的代码是相同的,不同的只是主体部分。 因为每个脚本代表着不同的UI界面,所以其包含的具体对象也就不同,主体部分正是包含了对脚本中所有对象的获取方法。 下面脚本主体部分的代码示例: publicWFramegetTopContainer()throwsException{ returnnewWFrame(config.getObj(this,"TopContainer")); } publicSWTTextgetTxtUserName_LogonForm()throwsException{ returnnewSWTText(config.getObj(this,"txtUserName_LogonForm")); } publicSWTTextgetTxtPwd_LogonForm()throwsException{ returnnewSWTText(config.getObj(this,"txtPwd_LogonForm")); } publicWButtongetBtnLogon_logonForm()throwsException{ returnnewWButton(config.getObj(this,"btnLogon_logonForm")); } publicWButtongetBtnCancel()throwsException{ returnnewWButton(config.getObj(this,"btnCancel_logonForm")); } 从上图主体部分的代码可以看到,getXXX()方法会返回不同的对象类型,比如Button,Label,或者Text,而应该返回哪种对象类型,是由class、TestobjClassName以及Role这些对象属性共同决定的,即这三种属性的一个特定组合匹配一个特定的返回类型。 再配合某类对象可以用来动态查找的属性,我们就可以进行动态而精确的匹配。 当然,这类可以用作动态查找属性的属性有很多,因不同的对象类型而异,可以在对象匹配文件中自己指定——对象匹配文件以XML格式存储了区分各种对象类型需要的属性信息。 下图展示了对象匹配文件中Button对象的属性匹配信息,我们根据Button对象的class、TestobjClassName以及Role属性来辨别一个Button对象,再通过比较属性text,to
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- RFT 静态 方法 转动