一种高效的对象缓存机制在测试框架中的应用
使用 RFT 进行 GUI 测试自动化
自动测试解决了传统手工测试中的很多问题,把测试人员从繁重而重复的测试工作中解脱出来,节省了很多人工时间。RFT 是一种非常有效,使用方便的测试自动化工具,在很多项目中得到应用。用户可以选择使用 RFT 进行录制播放的方法来进行自动化测试,也可以选择编写测试框架,并在框架的基础上编写脚本的方式实现。前者的缺点很明显,就是在测试的应用发生变化时需要重新进行录制,而当变更涉及到很多 case 时,这将带来很大的工作量,这也许比手工测试花费的时间还长。所以除非能避免这个问题,所有的项目都采用后者。在我们的项目中也是如此
Object Map
在 GUI 测试中,RFT 提供了两种获取对象的方法,一种是通过 ObjectMap,由 RFT 对需要测试的对象进行识别,用户可以根据需要选择把哪个对象引入测试脚本,RFT 自动生成 getter 方法供使用者调用。RFT 提供的录制播放功能也是采用 ObjectMap 来实现对象获取。此方法的优点是用户不用关心对象获取的细节。然而缺点与使用录制播放的方式类似,就是当测试的应用界面结构变化时,用户经常需要重新更新 ObjectMap,尽管 RFT 有搜索权重机制,一些微小的变化不会导致更新。而且用户经常会遇到无法使用 ObjectMap 获取的对象。
有很多关于如何使用 ObjectMap 的文章和资源,这里只给出一个简单的例子。
Microsoft word 2007 是一个非常适合做例子的应用。测试步骤共两步:
1. 打开 word 2007;
2. 在菜单栏中点击”View”,再点击”Home”。
图 1. “View”和“Home”在 word 2007 的 ObjectMap 结构中的位置
在 RFT 中新建一个脚本叫做”ObjectMap.java”,在自动生成的代码中,这个类继承自“ObjectMapHelper.java”。然后把 “View”和“Home”两个对象插入到这个脚本中。RFT 会自动为这两个对象生成 get 方法,并插入到 ObjectMapHelper.java 中。图2. ObjectMapHelper.java
ObjectMap.java 中的代码非常简洁 :
viewpageTab().click() 在菜单栏中点击”View”,homepageTab().click() 即点击”Home”对象。
图 3. ObjectMap.java
动态搜索
另一种获得对象的方法就是动态搜索,使用动态搜索,就是用户自行使用 RFT 提供的搜索方法,从测试应用结构中的某个节点根据提供某些条件进行搜索获取对象。这个方法自由,灵活,而且能得到一些 ObjectMap 无法获取的对象。在我们的框架中,使用的就是这个方法。但这个方法在测试的应用对象结构复杂时,性能比较差。因为搜索将遍历起始节点下所有的对象层次结构。
这里提供的动态搜索的例子可以完成和使用 ObjectMap 完全相同的功能。不过限于篇幅,只提供了获得”View”按钮的例子。
代码 1. ViewButton.java:
| package dynamicsearch; …… public class ViewButton extends GuiTestObject { public ViewButton( TestObject testObject ){ super( testObject ); } public static ViewButton getViewButton(){ //获取根测试对象,从该对象开始查找需要的对象 RootTestObject root = RootTestObject.getRootTestObject(); //通过匹配ViewButton的属性来找到该对象 TestObject to[] = root.find( RationalTestScript.atDescendant( ".class", ".Pagetab", ".name", "View" ) ); return new ViewButton(to[0]); } } |
代码 2. DynamicSearch.java
|
package TestScript; public void testMain(Object[] args) |
高效的对象缓存机制
我们想使用动态搜索,但又想有 ObjectMap 的性能优点,于是我们采用了一种高效的对象缓存机制来对动态搜索获取的对象进行缓存和管理,使动态搜索获取的对象可以做到只需要搜索一次,而不用再每次需要使用的时候都进行搜索,这样就减少了大量的搜索时间,提高了性能。同时,在对对象的管理上,该机制采用了类似 ObjectMap 的层次结构,相当于一种简化的 ObjectMap。
* 该缓存机制具有与 ObjectMap 类似的层次结构
* 在这个机制中,只有需要的对象才会被存储
* 如果应用的变化没有造成缓存中存储的对象层次之间的变化,不需要调整缓存部分的代码。
* 缓存是全局的,如果对象层次结构发生,对程序的修改将基本被限制在缓存这层代码中,不会造成整个框架和所有测试脚本需要修改的惨状。
该机制的实现并不复杂。
* 使用被测试应用的顶层窗口对象作为根对象存储。
* 把隶属于该窗口的对象作为根对象的成员变量存储 .
* 按照 ObjectMap 的层次顺序存储对象,下层的作为上层的成员变量。没有用到的对象层次可以忽略。
* 在所需该对象为 Null 的时候,getter() 方法调用动态搜索方法获取对象
* 当所需对象不为 Null 的时候,getter() 方法直接返回缓存中存储的对象。
这里将使用这种缓存机制实现刚才的例子,如果这个操作只被调用一次,那么缓存机制将不会与普通的动态搜索方法有区别。但如果这是一个被频繁调用的操作,那么缓存机制将体现出它的价值,极大的减少对象获取的时间。
建立一个名为 WordWindow 的对象存储所有 MS Word 2007 窗口的直属子对象。这些子对象也可存储属于他们的子对象。
代码 3. WordWindow.java
| package appObject; …… //WordWindow类包含所有在Word窗口中要操作的子对象及该窗口本身 public class WordWindow { private GuiTestObject viewButton; private GuiTestObject homeButton; public GuiTestObject getViewButton(){ if(viewButton == null ){ RootTestObject root = RootTestObject.getRootTestObject(); //通过匹配ViewButton的属性来找到该对象 TestObject to[] = root.find( RationalTestScript.atDescendant( ".class", ".Pagetab", ".name", "View" ) ); return new GuiTestObject (to[0]); } return viewButton; } public GuiTestObject getHomeButton(){ if(homeButton == null ){ //通过匹配HomeButton的属性来找到该对象 RootTestObject root = RootTestObject.getRootTestObject(); TestObject to[] = root.find( RationalTestScript.atDescendant( ".class", ".Pagetab", ".name", "Home" ) ); return new GuiTestObject(to[0]); } return homeButton; } } |
CacheVer1.java: 使用缓存机制完成操作。
代码 4. CacheVer1.java
| package TestScript; …… //如果该测试用例需要对View按钮和Home按钮做更多的操作,可以直接从缓存中获取这两个对象,而无需再一次搜索 public class CacheVer1 extends CacheVer1Helper { public void testMain(Object[] args) { WordWindow word = new WordWindow(); word.getViewButton().click(); word.getHomeButton().click(); } } |
接下来将探讨一下如何在该对象缓存机制的基础上进行改进,再进一步的提高动态搜索的性能呢?我们注意到,动态搜索的原理类似于从某个节点开始,对这个节点以下的所有节点进行遍历,把所有满足条件的对象都返回。一般在动态搜索前,我们会获取测试应用的窗口作为根节点,对所有需要的对象进行搜索,这样的操作十分耗时。现在我们有了一个按照对象层次存储的缓存,就可以利用它来减少搜索的时间。
具体实现如下:
* 使用被测试应用的顶层窗口对象作为根对象存储。
* 在 ObjectMap 中分析需要被查找的对象
* 如果有一组对象具有相同的祖先节点,那么把离它们最近的祖先节点对象存储到 cache 中
* 当需要获取这组对象中的某一个时,先或者事先存储的祖先节点,然后从这个祖先节点开始进行动态搜索。
这种机制会减少很多动态搜索所涉及到的层次,进一步减少了动态搜索需要的时间。
我们建立一个新的 WordWindowVer2 对象作为缓存对象。我们的示例很简单,只有一个缓存对象。在真实的项目中,根据对象的复杂程度,我们会需要一种设计模式更好的组织更多的缓存对象。
代码 5. WordWindowVer2.java
|
package appObject; public WordWindowVer2( TestObject testObject ){ |
代码 6. CacheVer2.java
| package TestScript; …… // 获取 Word 窗口,并点击窗口中的 Viewn 按钮和 Home 按钮 public class CacheVer2 extends CacheVer2Helper { public void testMain(Object[] args) { WordWindowVer2 word = WordWindowVer2.getWordWindowVer2("Document1 - Microsoft Word"); word.getViewButton().click(); word.getHomeButton().click(); } } |
如果我们会用到很多 MS Word 菜单按钮的,这种缓存机制将从按钮 group 对象开始进行搜索按钮对象,搜索的速度会变得非常迅速。同时这种机制支持缓存从不同的 Word 窗口获取按钮对象。
在实际的项目中,根据 IBM framework 框架,对象搜索方法会被包装成一个通用方法,并且获取的对象将在测试框架的 Task 层中使用,而不是直接在脚本中调用。
这种缓存机制节省了大量的对象搜索时间,提高了开发调试以及运行的效率。我们在自动测试脚本的运行中,不会在每次需要获取对象时都要等待到不耐烦地地步。我们也可以在更短的时间内让计算机运行更多的测试脚本。
该机制在测试框架中还起到了隔离对象获取和对象调用的分层目的,在缓存的对象代码中,提供了 getter 方法供上一层代码调用,而将从缓存中还是通过动态搜索获取对象的过程封装在缓存这一层代码中。
需要注意的问题是,当缓存中的对象不再指向一个真实的 GUI 对象时,一定要释放该对象。直接把它置成 Null 即可。否则从缓存中获得的将是一个过期的对象,造成脚本失败。
总结
本文提供了一种高效的缓存机制,可以使得使用动态搜索的 RFT 测试框架性能获得很大改善,并能够很好的管理获得的测试对象。我们的项目在没有使用这种机制前,一直为动态搜索的性能问题所困扰,使用该机制后,不论是脚本调试还是运行,都获得了令人满意的速度。同时在测试框架和测试脚本中,建立了清晰的代码层次,隔离了测试对象的获取和使用,使得框架和脚本代码的编写更加简洁,逻辑更加清楚。
免责声明
本文包含解决方案。IBM 授予您(“被许可方”)使用这个解决方案的非专有的、版权免费的许可证。然而,解决方案是以“按现状”的基础提供的,不附有任何形式的(不论是明示的,还是默示的)保证,包括对适销性、适用于某特定用途或非侵权性的默示保证。IBM 及其许可方不对被许可方使用该软件所导致的任何损失负责。任何情况下,无论损失是如何发生的,也不管责任条款怎样,IBM 或其许可方都不对由使用该软件或不能使用该软件所引起的收入的减少、利润的损失或数据的丢失,或者直接的、间接的、特殊的、由此产生的、附带的损失或惩罚性的损失赔偿负责,即使 IBM 已经被明确告知此类损害的可能性,也是如此。
转自:http://www.51testing.com/html/90/n-132790.html

浙公网安备 33010602011771号