RFT 描述性编程
1 问题与背景
在RFT中,识别web对象的方式有两种,静态识别-通过对象库的方式,动态方式-通过RFT 的find方法或者通过child逐级查找方式来识别。
在动态识别时,存在这样三个问题:
1. 识别速度慢,可能需要4-5秒才能完成一个对象的识别。
2. 对编写不
规范
编程规范下载gsp规范下载钢格栅规范下载警徽规范下载建设厅规范下载
的html对象,很难识别。对于没有id, name 属性的对象,很难准确识别。
3. 脚本编写复杂,不易读。
WLink.findHtmlLinkFromTextProp("登录",browser).click()
WTextField usernameText=new WTextField("username", ".name", "Html.INPUT.text", browser);
usernameText.setText("abc");
特别是在识别文本框时,需要通过查看html代码的方式来查看该对象的id, name 属性。
在识别一些编写不规范的html对象的时候,如图片等,如果没有id, name 属性,将很难准确识别。
可以通过增加以下3个特性,来解决这些问题:
· 缓存节点路径,提高识别速度。
· 根据页面信息来描述对象,增加识别手段。
· 描述性编程,使脚本更易读。
2 描述性编程
在RFT中,识别web对象的方式有两种,静态识别-通过对象库的方式,动态方式-通过RFT 的find方法或者自定义的查找方式来识别。
在IBM的框架中,如要查找一个输入框,其属性 .name 的值为 username:
WBrowser browser = new WBrowser(BrowserOps.findBrowser());
WTextField usernameText=new WTextField("username", ".name", "Html.INPUT.text", browser);
usernameText.setText("123");
首先获取到浏览器对象,然后在浏览器中不断的递归查找节点,当其节点的属性 符合 .name 的值为 username, .class的值为 Html.INPUT.text 时,即可返回。
在QuickTest中,可以通过描述性编程的方式来动态识别一个Web对象:
Browser().Page().WebEdit("name:=username").set "123"
相比之下,QTP的这种描述对象的方式,将属性名称与值作为同一个字符串输入,并且可以输入多个属性名称与值作为参数,使用起来更加方便。
在RFT中,也可以使用同样的描述性编程的方式来识别一个对象:
Browser browser = new Browser();
browser.getWebEdit(".name:= username",".class:=Html.INPUT.text").setText("123");
或者
browser.getWebEdit(".name:= username").setText("123");
实现的步骤为:
2.1 定位浏览器对象
RFT有一个特性,新打开的最后一个浏览器,通过 getDomains() 查找时,一定是最先找到的。
通过getDomains()获得Html进程后,通过getTopObjects()方法,就可以获得浏览器对象。
public static TopLevelTestObject getLastBrowser(String toType){
RationalTestScript.sleep(0.5);
DomainTestObject domains[] = RationalTestScript.getDomains();
String domainName;
TestObject[] topObjects;
Hashtable ht;
String sClass;
boolean visible=false;
for (int i = 0; i < domains.length; i++){
try{
domainName=domains[i].getName().toString();
if (domainName.equalsIgnoreCase("Html")){
topObjects = domains[i].getTopObjects();
if (topObjects != null){
for(int j=0;j
表格
关于规范使用各类表格的通知入职表格免费下载关于主播时间做一个表格详细英语字母大小写表格下载简历表格模板下载
.class
.id, .name
getRowCount
getColumnCount
getCell
...
Html.TABLE
WinButton
弹出框按钮
.class
.text
click
Html.DialogButton
WinStatic
弹出框文本信息
.class
(index)
getText
Html.DialogStatic
public class WebElement {
GuiTestObject webElement=null;
public WebElement(TestObject to){
if (to==null){
webElement=null;
}else{
if (to instanceof GuiTestObject){
webElement=(GuiTestObject) to;
}else{
logger.error("TestObject class is not GuiTestObject: "+to.getObjectClassName());
webElement=null;
}
}
}
}
public class WebEdit extends WebElement{
public WebEdit(TestObject to) {
super(to);
}
public void setText(String s){
this.click(); //activate text field
this.clearText();
this.inputChars(s);
webElement.unregister();
}
}
2.3 根据描述属性查找对象
识别一个Web对象,可能需要使用1个或者多个属性。查找对象时,通过getChildren()方法来逐层查找,匹配这些属性,当属性值完全符合时,则认为已经找到。
1 动态个数的输入参数,可以使用 String ... args 来解决。描述属性的key和value值通过 := 来分割。如:
browser.getWebEdit(".class:=Html.INPUT.text",".name:=username").setText("123")
2将默认的.class属性写入,减少手工输入代码的工作量
public WebEdit getWebEdit(String... properties) {
HashMap descMap = WebUtil.getDescMap(properties);
if (descMap != null){
if (!descMap.containsKey(".class")) {
descMap.put(".class", "Html.INPUT.text");
}
}
WebElement testObj=getWebElement(descMap);
if (testObj==null){
return new WebEdit(null);
}
logger.debug("found the obj");
return (WebEdit)testObj;
}
2 先将字符串数组 properties解析出属性的键值对,存放到HashMap中
`
/**
* 将 ".class:=Html.A",".text:=链接" 的字符串描述转换成 HashMap
* @param properties
* @return
*/
public static HashMap getDescMap(String[] properties) {
HashMap descMap = new HashMap();
String[] tempAttrbute;
for (String property : properties) {
tempAttrbute = property.split(":=");
if (tempAttrbute.length == 2) {
descMap.put(tempAttrbute[0], tempAttrbute[1]);
} else {
logger.error("Error: description string is invalid: "
+ property);
return null;
}
}
return descMap;
}
3 按照属性在页面上查找对象
查找对象的方法,使用递归查找child的方法,其好处是, 可靠性较好,只要页面上存在指定的对象,则必定可以查找到。缺点就是查找速度慢,无论是按照深度优先,还是按照广度优先查找,因为必须将前序节点的属性都逐一进行检查。不过这个问题可以通过后续的缓存路径的方式来解决。
public static TestObject getChildObjects(HashMap descMap, TestObject parent) {
Testobject testObj=null;
if (isMatchObject(parent,descMap)){
return parent;
TestObject[] childObjs = parent.getChildren();
// 循环查找直接子对象
for (int i = 0; i < childObjs.length; i++){
testObj =getChildObjects(descMap,childObjs[i);
}
return testObj;
}
4 属性匹配。
优先匹配 .class 属性
public static boolean isMatchObject(TestObject testObj,HashMap descMap) {
String actualClass=null;
String expectVal;
String actualVal;
Hashtable proTable = testObj.getProperties();
if (descMap.containsKey(".class")){
// 优先匹配 .class属性
actualClass=proTable.get(".class").toString();
if (! actualClass.equalsIgnoreCase(descMap.get(".class"))){
return false;
}
}
boolean isMatch=true;
for(String property : descMap.keySet()){
if (!property.equalsIgnoreCase(".class")){ //逐个遍历剩下的属性
expectVal = descMap.get(property).trim();
if (proTable.containsKey(property)){
actualVal = proTable.get(property).toString().trim();
isMatch = expectVal.equalsIgnoreCase(actualVal);
}else{
isMatch=false;
}
if (! isMatch){
break;
}
}
}
return isMatch;
}
5 增加index的识别属性。
通过描述的属性,可能会查找到多个符合属性的元素,可以增加index的属性,默认查找第一个,如果指定了index的值,则按照index的值返回对应的元素。
3 缓存节点路径
通过深度优先或广度优先的顺序来查找页面节点,速度必然会慢。为了加快速度,可以将第一次查找到节点的路径保存起来,第二次查找时,则根据之前保存的路径直接比对,以此提高二次查找的效率。
对每一层的节点按照顺序进行编号,每一层的起始编号均为0。如上图,username的输入框的路径为0-1-0-0-0, 密码的输入框的路径为0-1-0-0-1。
节点路径会保存在缓存文件(key-value的txt文件)中,key和value之间用两个#号分割,key为Web对象的描述属性按照指定顺序连接起来的字符串, value为节点的数字路径。
.class:=Html.INPUT.text,.name:=userId##0_3_0_1_0_4_3_0_0_0_0_0_3_0
.class:=Html.INPUT.password,.id:=oldPassword##0_3_0_1_0_4_3_0_0_0_1_0_3_0
由于同一个缓存文件中,并不能区分web对象所在的页面,所以,如果两个不同的页面上,都存在同样属性的web对象,如 "确定" 按钮,就会出现互相覆盖,导致缓存错误的情况。
另外在有些业务系统中,每个用户的权限不同,或者业务
流程
快递问题件怎么处理流程河南自建厂房流程下载关于规范招聘需求审批流程制作流程表下载邮件下载流程设计
不同,同一个对象在同一个页面上的路径也会不同, 如菜单,每个用户登录后的菜单顺序都会不同。
因此就需要采取以下措施来避免:
1 将这两个对象保存到两个不同的缓存文件中。
2 增加额外的描述属性来区别这两个对象。如增加 descName, extName 这两个属性,它们不参与对象的属性匹配,但是会保存成不同的key值,形成两条不同的记录。
查找对象时的逻辑为:
1 根据对象描述属性,生成唯一的缓存key值
2 检查缓存文件,缓存文件中是否存在该key值。
2.1 如果存在该key值,取出value--节点的路径
2.1.1 在页面上,直接根据路径,找到对应的节点
2.1.2 检查找到的节点是否符合描述的属性,
2.1.2.1如果符合描述的属性,则返回该对象。
2.1.2.2 如果不符合描述的属性,根据缓存路径查找对象失败,跳转到根据属性查找对象流程
2.2 如果不存在该key值,则根据缓存路径查找对象失败,跳转到根据属性查找流程
3 根据描述属性,逐级在页面上查找该对象
4 如果查找到符合属性的对象,将该节点的路径保存到缓存文件中,然后返回该对象进行后续操作
5 如果未查找到符合属性的对象,提示错误。
通过缓存来查找web对象,大约0.5秒就能查找到正确的对象,足够满足自动化测试的需要。
public static WebElement getChildByCachePath(TestObject testObj,HashMap descMap,String cachePath) {
if (cachePath!=null && cachePath.length()>0){
String[] cacheStep=cachePath.split("_");
int seq=0;
for(int i=1;iseq){
testObj=tos[seq];
}else{
logger.warn("not found object by cache path: level="+i+" child cnt="+tos.length+", but expect sequence="+seq);
return null;
}
}
logger.debug("found one");
try{
if (isMatchObject(testObj,descMap,cachePath)){
return subTypeWebElement(testObj);
}else{
logger.warn("found object by cache path, but not match:"+descMap.toString()+"\t"+cachePath);
return null;
}
}catch(Exception e){
logger.error("found object by cache path, but excption");
logger.error("Exception:",e);
return null;
}
}
logger.debug("no cache path for: "+descMap.toString());
return null;
4 根据页面信息来描述对象
一个普通的文本输入框:
username: |___________|
这个文本框的html结构可能有这样几种形式:
uesername:/td>
| |
或者
username:
现有的识别方式,都要通过查看这个输入框的 id, name 属性来识别。
对于这样一个输入框,人肉眼是如何来识别这个输入框的呢?必定是根据输入框前的文本信息"username",而不是该输入框的id, name 属性。
4.1 通过左边文字来描述对象
那么在程序中如何来通过左边的文字来识别一个文本框呢?
browser.getWebEdit("xLeft:=username").setText("Tom")
扩展一个描述属性,把左边文字作为web对象的一个属性,与其它源生属性同样的进行匹配。标识该属性为xLeft。
获取一个web对象的左边文字的逻辑:
1. 取到该web对象左边节点的 .text属性
2. 如果左边节点的.text属性不为空,则取出.text的值作为xLeft的值( 形如 username:
)
3. 如果左边节点的.text属性为空字符串或者无意义的字符串,则找到web对象的父节点的左边节点,再次判断。
4. 逐层向上递归,直到查找到有意义的字符串或查找到顶层节点。
由于RFT不提供获取左边节点的方法,只能通过 getChildren() 和getParent()这两个方法来辅助实现。不过由于已经缓存了节点的路径,所以很容易找到左边节点。
private static String getLeftString(TestObject testObj,String left,String pathPrefix){
TestObject parent = testObj.getParent();
String[] paths = pathPrefix.split("_");
if (paths.length<=2){
return left;
}
pathPrefix="";
for (int i=0;i=0;i--){
left=getNodeText(childNodes[i])+left;
if (left.trim().equalsIgnoreCase("*")||left.trim().equalsIgnoreCase(":"))
left="";
if (!left.matches("\\s*")){
if (isFilterString(left)){
left="";
}else{
break;
}
}
}
if (left.matches("\\s*")){
left=getLeftString(parent,left,pathPrefix);
}
}
return left.trim();
}
4.2 通过右边文字来描述对象
与获得节点的左边文字的方法类似。
4.3 描述一个表格
如下,在业务系统中常见的表格,如果要获取该表格某行的数据,或者某行中的链接、复选框,进行后续操作,那么就需要先查找到该table。
除了使用表格的id、name属性来识别该table外,还可以根据表格的第一个单元格的文字来识别,把这个属性定义为 xName, 如上面的表格,就可以识别成:
WebTable table = browser.getWebTable("xName:=序号")
代码如下。
private static String getTableName(WebTable webTable){
String name="";
String cellText = null;
int rowCount=webTable.getRowCount();
int columnCount =webTable.getColumnCount();
for(int i=0;i0){
name =cellText.trim();
break;
}
}
if (name!=null && name.trim().length()>0)
break;
}
return name;
}
5 解决其它问题
在业务系统中,经常为了达到各种效果,将页面上的一些元素隐藏起来。
RFT提供判断一个元素是否显示的方法:
(GuiTestObject)testObj).ensureObjectIsVisible()
这个方法在检查web对象时,会将该web对象置于当前显示区域内,并且在检查下拉框时,会逐个遍历下拉选项,使用时需要注意。
或者自己写方法来判断元素是否显示:
public static boolean isShow(TestObject to){
Hashtable htable= to.getProperties();
return isShow(htable);
}
public static boolean isShow(Hashtable htable){
if (htable==null){
return false;
}
String[] t2;
if (htable.containsKey("style")){
for (String sty:htable.get("style").toString().split(";")){
t2=sty.split(":");
if(t2.length==2){
if (t2[0].trim().equalsIgnoreCase("DISPLAY")){
if (t2[1].trim().equalsIgnoreCase("none")){
return false;
}
break;
}
}
}
}
return true;
}
如果对象的显示和隐藏是通过样式表中的class来控制,则不能获得该对象的display属性。此方法无效。
Html 0
head 0
body 1
form 0
div 0
text: username
0
input: password
1
本文档为【RFT 描述性编程】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
格式:doc
大小:367KB
软件:Word
页数:15
分类:互联网
上传时间:2013-08-30
浏览量:20