又是漫漫长夜,无心睡眠……这次文章是技术总结,不是随想,所以省去开头和暖场,直接切入正题。本文档对于如何使用RFT+XPATH提高自动化测试的执行效率提供了一种新的思路,就是缓存对象路径,(PS:没用过RFT做自动化测试的同学可以忽略了 ,用watir的同学也可以忽略了。)
界面对象识别及维护的费效比是UI自动化测试永远的话题
UI层的自动化测试,提高脚本的执行效率是永远的主题,我们必须把一轮脚本的执行时间控制在可接受的范围内,当脚本的总体执行时间很长时,我们就需要对其进行优化,优化的方法无非是三种,1、优化用例,减少无效用例的个数;2、提高脚本识别UI对象的效率;3、分布式执行。本文介绍的方法属于第二种,提高脚本识别UI对象的效率。
什么是RFT
RFT是IBM的一款自动化测试工具,它的作用和QTP类似,使用JAVA作为脚本语言,ECLIPSE作为IDE,优雅并且强大。RFT识别对象的核心方法是RootTestObject的find方法。我们做自动化测试的时候,通过对FIND方法的封装实现了自己的对象识别框架。但是find方法的执行效率问题一直是我的心病。之前的情况是查找对象时,执行一次find方法至少要4-6秒的时间,几百个用例执行下来执行时间就让人无法忍受了,十几个小时……只能晚上跑脚本,早上来看结果,真赶到上午发版的时候,根本指望不上自动化测试能帮上什么忙,所以必须要想办法缩短脚本执行的总体时间,提高对象的识别效率。
什么是XPATH
XPATH是一种XML的查询语言。简洁,高效。在IBM的开发者社区论坛里,有专门的一篇文章讲解如何结合RFT+XPATH来实现动态识别对象,很赞。这里给出文章的链接:《使用 XPath 在 Rational Functional Tester 中动态识别对象》,作者刘哲,段雪飞,邢静。在此表示感谢并崇拜一下。另外多说一下,IBM的开发者社区真的很有料,他们的文档库里关于自动化测试的文章建议大家通读,保证你获益匪浅。
XPATH识别对象的效率明显优于find方法,为什么?原因在于XPATH查找对象是基于某个对象的具体路径,而find方法是动态查找,也就是全局查找,这样遍历的对象多,必然就慢。举一个xpath的例子,来源于刚才的那篇文章,对于对象,使用XPATH来查找时路径可能是:"org.eclipse.swt.widgets.Shell[@captionText='Hello']/org.eclipse.swt.widgets.Button[@text='OK']"),而使用rft的find方法如果想实现同样的效果,就只能用atChild来拼了。写过RFT脚本的同学应该都有体会,事实上,如果在xpath中不使用路径而直接传入/::desander[@captionText='Hello'],那么他的执行效率和find的全局查找是类似的。
问题及解决方案
但是XPATH虽然快,可是仍然有缺点,那就是路径的维护问题。在一个自动化测试工程中,我们可能会使用到近百个测试对象,我们当然可以把每个对象的XPATH路径存到文本文件里,但问题是,如果程序的页面结构发生变化了,怎么办,这么多的缓存路径改起来不是闹着玩的,而且排查错误也不容易。所以我一直在寻找一种方法,能结合RFT动态识别和XPATH效率快两个优点,并且维护简单,后来就有了这个想法:把一个测试对象的父对象的class属性依次读取出来,拼成该对象的一个路径并写入到缓存文件中,当脚本在识别对象时,首先从缓存文件中把路径读取出来,并传递给XPATH去识别,如果对象能识别到,则执行操作;如果识别不到,则重新执行find方法进行识别,并把识别到的对象的路径重新更新到缓存路径中,如果find方法也找不到对象,就返回null,此时就需要人工介入排查识别的原因了。这种做法就是我标题中提到缓存对象路径的作法。
关于缓存文件的存储格式,我采用的策略是为每一个UI层的测试对象分配唯一个的objectID作为标识。规则是脚本名称 + object_id + objecg_value。其中object_id和object_value是你在识别测试对象时使用的属性。写过RFT脚本的同学应该都懂,不懂的就给我发私信吧,因为也不是一句两句能解释清楚的,毕竟这不是一篇教程,大家见谅;
缓存对象路径的做法,其实核心就是两点:
1、在识别测试对象时,永远先从缓存文件中读取对象路径并由XPATH识别;
2、当路径失效或者不存在时,由自动化测试代码自动更新路径,不然想自己手写XPATH路径的同学您最好先考虑清楚。
以下是几个核心代码的实现,供大家参考。(PS:想要直接拷代码的同学可以忽略下面的内容了,因为你没有这些代码底层的支持代码,拷过去也没用的)
通过递归读取对象路径的方法:
private void addPropertyToCatch(TestObject to, StringBuffer str) { str.append("child::").append(_class); // 在IE的document模型中,所有的索引都是从1开始的 } |