IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Rational  >

基于 Rational Functional Tester 的测试脚本自动修复技术

提高自动化测试效率与测试质量

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


郭 鹏 (pengguo@cn.ibm.com), 软件工程师, IBM

2008 年 3 月 17 日

软件测试是保证软件质量的重要手段。软件自动化测试对于提高测试效率与测试质量起着重要作用,但由于被测系统的复杂性,使得软件自动化测试变得难以实施。本文就是基于 RFT 的三层测试框架的基础上,提出了脚本修复技术,提高脚本的使用率。

1 基于 RFT 三层测试框架

由于 GUI 软件测试自身多变的特点,目前在软件测试脚本生成技术上主要采用分层的概念,尽可能低地降低界面变化对测试脚本的影响。本文就是基于 RFT 的三层测试框架的基础上,提出了脚本修复技术,提高脚本的使用率。下图就是典型的三层测试框架结构。


图 1. 三层测试框架结构
三层测试框架结构

1.1 对象的表示

在描述 GUI 对象的表示时,本文将使用 Rational Functional Tester 来获得被测程序的所有对象。图描述了 Rational Functional Tester 对捕捉到的所有被测对象的组织。点击 MyNotePad 程序中 About 菜单中 AboutMyNotePad 菜单项时弹出的 About Dialog 对话框。图描述了 Rational Functional Tester 中 MyNotePad 程序中的对象组织结构。

可以看到,MyNotePad 被组织成了一棵根节点是“MyNotePad”的树,它有 3 个子节点,分别为 About Dialog 对话框、MenuBar 和 TextArea。About Dialog 对话框有 2 个儿子节点,分别为 OK 按钮和一个 label 存有 about 信息。MenuBar 有 3 个子节点,分别为 File、 Edit 和 About。TextArea 没有子节点。

可以看到,Rational Functional Tester 将 AboutDialog 对话框作为根节点 MyNotePad 的直接子节点。事实上,Rational Functional Tester 会将所有的弹出窗口存为根节点的直接子节点。使用这种组织结构,不可能从图中了解到弹出窗口和其它 GUI 组件之间的关系。比如,AboutDialog 对话框应该在什么状况下弹出?对于这种问题无从得知,所以无法完整的表示 GUI 上对象之间的关系。为了解决这个问题,本文调整了被测程序的 GUI 对象组织结构,形成了一颗新的树形结构的图,称之为对象树。如图所示。这样的树形结构可以很容易的看出各个组件之间的依赖关系。


图 2. 树形结构
树形结构

图 3. 各个组件之间的依赖关系
各个组件之间的依赖关系

对象层在这一层中,要将元素名与测试内部对象名独立出来。界面对象映射脚本实现界面对象逻辑名与被测试软件真实界面对象之间的映射关联。界面对象映射脚本是自动化测试的关键,它可以使测试工程师和自动化测试脚本开发工程师进行工作分工,实现测试开发与软件开发的同步。在软件需求确定后,测试工程师就可以开发测试脚本;而自动化工程师根据与测试工程师约定的界面对象的逻辑名来编写测试脚本。最后,测试工程师开发的测试脚本与自动化工程师开发的测试脚本通过界面对象映射脚本关联成一个有机的自动化测试脚本集。

事件层事件层主要实施一些常用的测试操作,如文本内容录入、菜单选中、列表框内容选择、按钮单击、预期输出结果验证等。它是和对象相互对应的,每一个对象都应该有自己的事件集合。对常用的软件使用操作,可以针对每个类型控件设计一个通用脚本,控件识别 ID 和相关联操作(单击、输入、选中等)作为该脚本的输入参数。对测试预期结果验证也可以使用类似的方法设计针对特定控件的验证脚本,不同之处在于其输入参数是验证的属性和验证结果值。由于所有软件的使用和测试都是这些基本操作的组合,因而在不同的项目之间,重用脚本是一致的。不同的是测试所使用的测试数据从不同的数据文件内读取,因此,这些脚本可以在不同的项目中实施共享,实现一次编写,多处共享,减少脚本的数量,从而降低脚本的维护工作量。同时要将数据分离出来,数据可以来自文件或者自动生成。因此要在事件层中为数据的输入和输出提供接口。

脚本层测试脚本实施对特定功能点和业务功能的测试。是一种针对特定的、被测试软件项目的脚本。其主要功能包括:从测试数据文件内读取测试数据。调用重用脚本和对象映射脚本将这些数据输入到被测试软件的特定对象中。并验证测试结果与预期输出的一致性。记录 Log 和 Bug 现象。它是由重用脚本依据一定的业务流程和特定操作流程组成。





回页首


2 GUI 空间依赖关系的引入

在同一 GUI 界面的控件之间或不同界面的控件之间,都有可能存在一些相互关联,相互依赖的关系,称为 GUI 控件依赖关系。依赖关系在具体表现形式为:一个控件的执行是另一控件执行的结果或者导致了另一控件的执行。例如,本来菜单项是不可操作的,对界面上某一个 GUI 控件操作后,激活了菜单项。

控件之间的关联关系是通过用户和控件的交互或控件之间的消息而激发的。而想要从 GUI 软件界面中直接获得控件之间的依赖关系是不实际的,并且很难保证正确性和完整性。因此,在实现时往往需要根据系统的需求和软件设计说明书,人工进行调整。为了方便确定控件之间的关联关系,根据各个控件的类型特点,大致可把控件分为下面两类:

行为控件 如界面上的菜单,按钮等控件,用户与之交互会触发某些事件。

信息控件 如文本框,编辑框等控件,它们可以接受用户的输入数据或根据某些控件条件输出数据。

一般来说,行为控件的执行和一个或多个信息控件相依赖,信息控件为行为控件的执行提供输入或输出数据。

2.1 事件依赖关系图

事件依赖关系序列图可以表示控件之间的依赖关系。它由有限个 (e 1,e 2...e n) 事件序列组成,每一个节点都表示一个对象控件对应的事件,有向边则表示了程序执行时界面控件之间的执行时序和之间的依赖关系。

事件依赖关系序列图其实就是以图形符号的方式形式化的来表示界面控件之间的依赖关系的有向图。其中用 ei 来表示一个事件,其属性包括对象元素名称,输入格式,输入参数,前驱顶点,后继顶点等;有向边可用“有向边”表示,表示系统中控件执行的时序和事件路径;依赖关系可用“依赖关系”表示,表示同一界面事件之间的依赖关系;用“省略事件”表示事件 e 1 到事件 e n 省略了若干事件。关联图中如果一个顶点没有前驱顶点,则叫做起点;如果一个顶点没有后继顶点,则叫终点。一个完整的事件依赖关系图,必须能够从起点走到终点,表明了该事件序列是合法的,可达的。

下图所示为一个简单的事件依赖关系序列图。其中 e 1 为起点,e n 为终点,e 2 事件制约着 e 3 事件,e 3 事件依赖着 e 2 事件。


图 4. 一个简单的事件依赖关系序列图
一个简单的事件依赖关系序列图

在事件依赖关系序列图中,依赖关系是事件序列能否正常运行的关键。如果能保证在有依赖关系的事件 e i 出现之前,其依赖事件 e j 已经出现,这样就可以保证 e i 的正确运行。通过依赖事件序列生成算法,可以找出 e i 的所有依赖事件 e j,并将 e j 写入到的 e i'e i' 为对象 i 的默认操作,该操作包含了对象 i 的所有依赖事件。如图 5 所示用 e i' 保证了在调用对象 i 之前,所有依赖事件已经出现。对于 e i 其依赖事件有两类来源:

  • 父节点所有子节点都依赖其父节点,只有父节点的对象处于活动状态,子节点的事件才能被调用。
  • 事先指定好的依赖节点测试人员往往需要根据系统的需求和软件设计说明书,人工进行调整,在对象树中将控件 A 所依赖的控件节点信息,写入到该控件 A 的依赖节点列表中。

图 5. 依赖事件关系图
依赖事件关系图

设集合 I 保存测试对象 O i 的所有依赖节点,依赖事件生成算法的步骤如下:

  算法输入:对象树状信息文件

  算法输出:生成有依赖关系对象的依赖事件

  步骤 1:输入对象树状信息文件;

  步骤 2:遍历对象树中所有节点;

  步骤 3:从对象集合中找到对象 O i,并将其标识为已访问,其父节点为 O j,其依赖节点为 O k, I = I ∪ {O j,O k};

  步骤 4:访问集合 I,如果集合为空,则转入步骤 9;

  步骤 5:依次访问集合 I 中的对象,并重复步骤 3;

  步骤 6:当集合 I 不再变化时,则找到对象 O i 所有依赖节点;

  步骤 7:访问集合 I 中对象,获得层级码,和该对象的默认事件;

  步骤 8:将默认事件按照层级码的方式组织起来,生成 e i'

  步骤 9:如果对象树中仍有未访问节点,清空集合 I,转到步骤 3;

  步骤 10:至此,遍历结束,生成所有有依赖关系对象的依赖事件 e j',算法结束。





回页首


3 脚本修复技术的提出

脚本修复技术是修复非法的事件序列,使得这些脚本可以在修改过的 GUI 上执行。表 1 中将 GUI 的变化主要是由 3 类构成的,对象的增加(例如增加了新的按钮),对象的删除(原有按钮被删除),对象的修改(原先的按钮变成了菜单选项)[30]。如果将这三类分析清楚,那么脚本修复功能就能够实现。除此之外,还有考虑依赖关系,因为对象如果存在被依赖关系,那么它的改变势必会对其产生依赖的对象产生一定的影响,因此可以将 GUI 的变化分为六类。下面分别介绍这六种状况。

表 1 GUI 的六类变化

对象变化类型 是否被依赖
增加对象 独立
增加对象 存在被依赖关系
删除对象 独立
删除对象 存在被依赖关系
修改对象 独立
修改对象 存在被依赖关系

3.1 增加对象

(1) 该对象不存在被依赖关系

存在事件序列 (e 1,e 2...e n)。新增对象 i,如果在该序列中不存在对对象 i 的依赖事件,那么关于 i 的事件 e i 可以不放入该队列中。如图 6 中 “事件序列图 1 ” 所示,因此原事件序列可以不用改变。


图 6. 事件序列图 1
事件序列图 1

(2) 该对象存在依赖关系

存在事件序列 (e 1,e 2...e n)。如果在该序列中存在对对象 i 的依赖事件,即 e i 是某个事件运行的前提条件。那么关于 i 的事件 e i 要放入该队列中。将 e i 放入依赖事件之前,这样可以保证原理的事件序列可以正常运行。因为对象树被更新,所有对象的事件集合也会被更新。因此在事件序列中只需要将原来 e x(依赖于 e i),更换为 e x'e x' 为默认操作,包含了对 i 的操作)。如图 7 中 “事件序列图 2 ” 所示,e x' 替代了 e x 加入到新的事件序列中。


图 7. 事件序列图 2
事件序列图 2

可见对于新增对象,只要原有事件序列没有产生对其的依赖,原有事件序列依然可以正常运行。

3.2 删除对象

(1) 该对象不存在被依赖关系

存在事件序列 (e 1,e 2...e i...e n)。如果在该序列中不存在对对象 i 的依赖事件,那么关于 i 的事件 e i 可以直接从该队列中删除。如图 8 中 “事件序列图 3 ” 所示,因此原事件序列只用删除 e i 事件。


图 8. 事件序列图 3
事件序列图 3

(2) 该对象存在被依赖关系

存在事件序列 (e 1,e 2...e i...e n)。如果在该序列中存在对对象 i 的依赖事件,那么除了关于 i 的事件 e i 要被删除之外,同时要将 e x 更改为 e x'e x' 为默认操作,在默认操作中也删除了对 i 的依赖事件)。如图 9 中 “事件序列图 4 ” 所示,新的事件序列中原有 e i 被删除。


图 9. 事件序列图 4
事件序列图 4

对于删除对象来说,判断该对象的依赖关系是修复事件序列的前提。而这些都可以通过更新对象层和事件层来完成。

3.3修改对象

(1) 该对象不存在被依赖关系

存在事件序列 (e 1,e 2...e i...e n)。如果在该序列中不存在对对象 i 的依赖事件,那么原事件序列不要更改,只需要更新 e i,因为对象树被更新,因此可以使用事件编辑器对 e i 进行更新。如图 10 “ 事件序列图 5 ” 所示,原事件序列没有改变。


图 10. 事件序列图 5
事件序列图 5

(2) 该对象存在被依赖关系

同图 5 所示,利用事件编辑器更新事件集合,原有事件序列不需要改变。

对于修改对象,其实可以看成是先删除对象,在新增一个对象。它的处理方式相对简单。

在脚本修复技术中,所有的事件序列的处理,都是要有对象和事件作为支持的,同时判断对象之间的依赖关系也是尤为重要的技术环节。所以在对脚本修复之间,需要动态的更新对象层与事件层的相关信息。

测试脚本修复思想产生的最初考虑就是以现有测试脚本为基础,尽可能使更多的原脚本可以稍加修改就可以用于频繁的回归测试过程。所以它完全可以作为一个现有方法的扩展,在计算能力足够强大或者时间要求相对宽松的条件下为了满足测试覆盖标准的要求(考虑到某一个或者某几个事件的重要性或者排除它们受到影响的可能,需要这些事件必须出现在回归测试集的脚本当中,就必须对该方法进行扩展)而进行应用。





回页首


4 脚本修复技术的实现

脚本修复的模块设计如下图所示,待测系统 1 为旧系统,待测系统 2 为新系统。当输入两个对象集合之后,通过对比,获得发生变更的对象 3;同时更新对象 3 的事件操作,并找出涉及到该对象 3 的测试脚本;最后对脚本进行修复,生成可重用的测试脚本。


图 11. 解析文件生成代码的流程图
解析文件生成代码的流程图

脚本修复器的实现要考虑的情况比较多,也比较复杂。因此对于脚本修复的实现主要从三个大类进行实现,即新增对象,删除对象,修改对象:

1)对新增节点的处理流程如图所示:


图 12. 新增节点处理流程图
新增节点处理流程图

具体算法实现如下:

// 更新脚本,参数为脚本名称,新增对象列表
Public boolean UpdateScript(String Testcasename,Element newlist)
{ 
    int size = getaddedNode.size();// 节点个数
    int i;
    String nodename;
    String depend;// 依赖节点名称
    Boolean update = false;// 更新标记位
    for(i=0;i<size;i++)// 遍历节点
    {
        if(depend.equalsIgnoreCase(“true”))// 存在依赖关系
        {
            if(operations.findefunction(nodename))// 找到该节点
            { 
                String default = nodename + “default”;// 默认函数名称
                operations.instead(nodename,default);// 用默认函数代替原函数
                update = true;
            }
        else
            continue;
    }
    Return update;// 返回 true 或者 false
}





回页首


5 脚本修复技术的应用

在 RFT 中 3 层框架的表示,appObjects 代表对象层,managers 代表事件层和对象层一一对应,最后是 testcases 脚本层。此外还有一些辅助的类用来进行其他操作。


图 13. RFT 中 3 层框架
RFT 中 3 层框架

5.1 对象层(appobject)

这里以一个简单的对话框来描述对象的树结构:对象捕获之后对象的层次结构。


图 14. 对象捕获之后对象的层次结构
对象捕获之后对象的层次结构

在 RFT 中的 GUI 树形图


图 15. 在 RFT 中的 GUI 树形图
在 RFT 中的 GUI 树形图

GUI 元素对象化:

所有被识别到的 GUI 被测对象都被记录在 Rational Functional Tester 的测试对象映射文件中。Rational Functional Tester 的测试对象映像文件是一个后缀为 rftxmap 的 XML 文件。通过对 XML 的解析,直接返回 GUI 对象。为下一步生成相关事件做准备。

public WButton getButtonOK()    
{    
    return new WButton(new TestObject(getMappedTestObject(“OK”));// 将元素对象化
}

RFT 中的显示


图 16. RFT 中的显示
RFT 中的显示

查看大图

5.2 事件层(managers)

基本事件的表示:

public void clickButtonOK()    
{    
   addDBDlg.getButtonOK().click();    
}    
public void DoubleclickButtonOK()    
{    
    addDBDlg.getButtonOK().doubleClick();    
}

default 事件的生成:

public void ClickBTNOKDefault()    
{    
    setTextIPAddressDefault();// 必填字段 1    
    setTextDBNameDefault();// 必填字段 2    
    setTextUserNameDefault();// 必填字段 3    
    setTextPasswordDefault();// 必填字段 4    
    clickButtonOK();// 保证所有的依赖关系都被调用,Button 此时可以被调用。    
}

5.3 脚本层 (testcase)

脚本层就是将事件进行拼装。可以安装事先指定的覆盖率自动生成。

public void testcase1(Object[] args)     
{    
    DBServerInfoTableViewMgr dbServerInfoViewMgr = new DBServerInfoTableViewMgr();
    // 调用事    件层对象       
    AddDBServerDlgMgr addDBServerDlgMgr = new AddDBServerDlgMgr();  
     dbServerInfoViewMgr.clickToolBar("addDBServer");       
     addDBServerDlgMgr.setTextIPAddress();       
     addDBServerDlgMgr.setTextDBName();       
     addDBServerDlgMgr.setTextPassword();       
     addDBServerDlgMgr.setTextComments();       
     ClickButtonOK();       
    // 安装覆盖率自动    生成脚本,也可以测试者手动添加。       
}

5.4 脚本修复 (update)

在进行用例修复的时候,通常对于删除对象最难处理。因此这里选择删除的对象进行处理。


图 17. 选择删除的对象进行处理
选择删除的对象进行处理

如图所示查找内容的输入栏被删除,其对象名称为 TextFieldFind。通过用例修复器对现有的用例集合进行修复:

(1)自动更新事件层,删除 TextFieldFind 的所有事件 , 同时更新所有的 default 操作,保证 default 操作一定可达;

(2)查找到涉及到 TextFieldFind 的测试脚本如 Testcase3;

(3)TextFieldFind 对象变化类型为删除,ButtonSearchNext、ButtonInstead、ButtonInsteadAll 对其有依赖关系;

(4)将存在有依赖关系的步骤,替换成默认操作,保证其可达性。

原测试脚本如下所示:

public void testMain(Object[] args)     
{
    MenuEditMgr menueditMgr = new MenuEditMgr();    
    SearchDlgMgr searchdlgMgr = new SearchDlgMgr();    
    menueditMgr.ClickMenubar("Edit");    
    menueditMgr.ClickMenubar("Edit->Paste");    
    menueditMgr.ClickMenubar("Edit");    
    menueditMgr. ClickMenubar("Edit->Instead");    
    searchdlgMgr.SetTextFieldFind("abc");// 事件被删除    
    searchdlgMgr.ClickBTNSearchNext();// 有依赖关系更新为 BTNSearchNextDefault()    
    searchdlgMgr.ClickBTNInstead();// 有依赖关系更新为 BTNInsteadDefault()    
    searchdlgMgr.SetTextFieldFind("123");// 事件被删除    
    searchdlgMgr.ClickBTNInsteadALL();// 有依赖关系更新为 BTNInsteadALLDefault()    
    searchdlgMgr.ClickBTNCancel();// 没有依赖关系不做改变    
}    

经过修复的测试脚本覆盖了旧脚本,执行这个脚本就可以完成回归测试,代码如下所示:

public void testMain(Object[] args)     
{    
    MenuEditMgr menueditMgr = new MenuEditMgr();       
    SearchDlgMgr searchdlgMgr = new SearchDlgMgr();       
    menueditMgr.ClickMenubar("Edit");    
    menueditMgr.ClickMenubar("Edit->Paste");    
    menueditMgr.ClickMenubar("Edit");    
    menueditMgr.ClickMenubar("Edit->Instead");    
    searchdlgMgr.BTNSearchNextDefault();    
    searchdlgMgr.BTNInsteadDefault();    
    searchdlgMgr.BTNInsteadALLDefault();    
    searchdlgMgr.ClickBTNCancel();    
}    





回页首


总结

一个完整的回归测试脚本集由修复的测试脚本和新生成的测试脚本两部分组成。使用测试脚本修复技术对已有测试脚本进行修复,提高了脚本的重用性。

最后结合一个实际的待测程序,进一步对脚本修复中技术细节进行研究与验证。最终达到了高效进行 GUI 软件测试、降低测试成本、提高 GUI 软件质量的目的。



参考资料

学习

获得产品和技术

讨论


关于作者

郭鹏,是 IBM 中国软件开发实验室的一名软件工程师。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

建议?







回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款