GAT:Windows平台下GUI软件自动化测试框架研究

2018-04-23 09:13钱汉伟
软件 2018年3期
关键词:测试工具测试用例脚本

钱汉伟

(江苏警官学院 计算机信息与网络安全系,江苏 南京 210031)

0 引言

图形用户界面(Graphical User Interface)的出现是软件发展历程中一个重要的里程碑,它给用户带来了极大的方便。图形用户界面是用户与系统进行交互的接口,接受用户或者其他系统输入事件,产生图形化的输出,用户对系统功能的调用和系统对用户的反馈都是通过GUI进行的。GUI软件接口越来越复杂,GUI部分占据了应用程序越来越多的代码量,相应的GUI软件测试也成了一项复杂而烦琐的工作。在软件复杂度增加的同时,软件开发周期却在不断变短,这给测试人员带来了巨大的挑战。

减少重复劳动,节省人力、时间,提高测试效率,减少人工测试中的错误,更好的协调开发工作量与测试工作量,进行GUI自动化测试是软件测试发展的一个必然趋势。

1 自动化测试工具概述

经过很多年的发展,目前已经有了相对成熟的GUI自动化测试工具了。这些工具有着自己的优点,同时也有一定的应用局限性。

SilkTest是Segue公司的GUI测试工具[1]。它运行在Windows平台,可以查询和测试使用标准MFC库生成的对象和GUI部件,也可以使用一些扩展的功能来测试非 MFC的 GUI部件。进行测试时,SilkTest提供捕捉/回放功能,能够与被测应用进行手工交互。一旦用户选择了待测的 GUI部件,SilkTest可以检查该部件的标识符、位置和物理标签。在用户继续操作应用时,SilkTest将用户动作翻译成基于对象属性或部件屏幕坐标的测试脚本。WinRunner是惠普公司一款图形界面自动化测试工具。用户可以简单的通过录制/回放的特点来完成一个测试用例。在录制过程中,它可以自动捕获检测当前的界面。把用户在界面上的操作自动转换成可描述性的语言和事件。QTP(Mercury QuickTest Professional)主要用于回归测试和同一软件的新版本的测试,在自动化测试工具中,QTP是一个典型的代表。

开源的 Java GUI自动化测试工具也有很多。Abbot框架能够对java应用进行GUI单元测试和功能测试[2]。它支持用户使用java代码编写测试脚本,也提供接口,让用户通过脚本来控制事件回放,以加强集成测试和功能测试。GUITAR是个GUI测试框架,提供解决GUI测试问题的统一的解决方法,该工具着力于开发新的基于事件的工具和技术。它包含一个测试用例生成器,可以生成测试用例,一个插件重放器负责在这些测试用例中运行测试。Pounder是实现自动java GUI测试的开放源码的工具。它包括不同的窗口,用户可以用来记录测试脚本,检查测试结果。Selenium是一个用于Web应用程序测试的工具[3],它的测试直接运行在浏览器中,就像真正的用户在操作一样。

很多开源的GUI自动化测试框架都是基于java的,其中对于Swing窗口工具组,它的组件自动产生各种事件来相应用户行为,通过注册监听器,我们可以监听事件源产生的事件,从而在事件处理程序中处理我们所需要处理的用户行为。但是Java运行在虚拟机上,对于更广泛的Windows的GUI测试没有太多的借鉴意义。大部分通用的自动化测试工具,如QTP,通常只能识别Windows标准的控件(按钮,滚动条,静态控件, 列表框, 编辑框, 组合框)信息,不能识别自定义控件[4-5]。因此,在有自定义控件的软件中,自动化测试用例功能点覆盖率比较低,无法使用通用自动化测试工具实现完全自动化。

本文提出一种新的测试工具 GAT(Graphic Automatic Tester)设计方法和技术,应用于Windows下被测程序自定义控件比较多,对GUI自动化测试覆盖率要求比较高的测试场景。插件方法可以让程序的设计和开发人员为测试工具的完善提供支持,测试工具能够识别所有的自定义控件,提供的测试脚本语言能够描述所有的自动化测试用例。采用关键字驱动技术,测试人员编辑测试脚本更加简单易懂,能大大提高整个自动化测试过程的效率。

2 相关技术

2.1 远程线程DLL注入

DLL(Dynamic Link Library)文件是 Windows操作系统中动态链接库文件,只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中。这种方式不仅减少了EXE文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用程序使用。“DLL注入”是指将一个DLL加载到一个正在运行的进程中,以达到执行DLL中的代码的方法[6]。

DLL注入的基本思路是要求目标进程中的线程调用 LoadLibrary函数来加载目标 DLL。为了防止一个进程破坏另一个进程的运行,Windows的大多数函数允许进程只对自己进行操作,有些函数却允许一个进程对另一个进程进行操作,比如 Create-RemoteThread,这是一个为调试程序设计的函数,却使我们能够在另一个进程中创建线程[7]。

远程DLL线程DLL注入一般来说可以分为 4个步骤:

(1)VirtualAllocEx函数,分配远程进程的地址空间中的内存。

(2)再使用WriteProcessMemory函数,将DLL的路径名拷贝到第一个步骤中已经分配的内存中。

(3)使用GetProcAddress函数,获取LoadLibrary(在Kernel32.dll中)函数地址。

(4)使用CreateRemoteThread函数,在远程进程中创建一个线程,它调用正确的 LoadLibrary函数,为它传递第一个步骤中分配的内存的地址。

2.2 COM 接口

COM(Component Object Model)是由 Microsoft提出的组件标准,它具有语言无关性和进程透明性的特点。COM规范的定义不依赖于特定的语言,因此,编写组件对象所使用的语言与编写客户使用的语言不同,只要他们都能够生成符合 COM 规范的可执行代码即可。在客户/服务器模型的软件结构中,运行在客户端的代码和运行在服务器端的代码,既可以在同一进程中,也可以在不同进程中。COM的语言无关性,使得 COM 接口函数可以被任何脚本语言所调用,具有广泛适用性。测试工具可以选择最合适的脚本语言调用COM接口。

COM组件有 IUnknown、IClassFactory、Idispatch三个最基本的接口类[8],分别是用于生存期控制、接口查询,创建 COM 组件和调度功能。IDispatch接口为自动化接口,有了IDispatch接口,脚本语言象VBScript、JavaScript等就能使用COM组件了。实现了 IDispatch接口的组件,其实就是自动化组件。IDispatch接口有4个函数,GetTypeInfoCount,GetTypeInfo,GetIDsOfNames,Invoke,Python 等解释语言的执行器就通过这仅有的4个函数来执行组件所提供的功能。

3 GA T总体设计

3.1 总体架构

为了便于系统的开发和维护,GAT采用分层设计,由上至下分为应用层,抽象层,功能层和插件层。应用层负责测试业务功能,保证测试用例的执行,主要包括测试脚本和测试数据两部分内容。抽象层按照被测试程序控件对象对底层函数进行了封装,屏蔽底层功能实现细节。功能层负责将DLL插件远程注入被测试程序,模拟用户向被测试程序发送鼠标和键盘消息。插件层主要负责被测试程序控件信息的获取[9-12]。

GAT启动,GetTestInfo插件被映射到被测程序空间后,收集被测程序相关信息。对象库中控件类(Button,)进行初始化,通过 PT代理调用GetTestInfo的接口函数,获得辨别控件对象句柄和取得对象的坐标位置等信息,并把信息赋值给对象库中对应类成员。测试脚本执行测试用例,按照要求在指定位置或者控件句柄发送键盘或者鼠标事件,完成用户单个行为模拟。图1展示了GAT的架构图。

3.2 架构分析

应用层主要包括了测试脚本和测试数据的组织和执行。为了便于共享测试数据,GAT将相关联数据统一管理起来作为测试数据文件,如被测程序需要用到的帐号数据另外放在测试数据文件中。测试用例表示复杂的场景时,GAT将其分割为多个测试用例并将这些测试用例分组到一个测试套件中,然后使测试脚本与该套件中的每个测试用例相关联。借助测试套件,对构成测试套件所测试的复杂场景的相关测试用例的执行进行规划、启动和跟踪。整个测试脚本由测试用例脚本和测试套脚本组成,一般测试用例脚本执行具体的测试用例操作,测试套脚本主要用于执行整个测试场景的一些初始化或结束被测程序的操作,使用Python语言编写。

图1 GA T总体架构图Fig.1 GA T architecture

抽象层封装了GetTestInfo插件和GUI测试库的底层函数,实现了简化测试脚本编写的功能。抽象层将被测试程序界面中存在的所有对象实体一一映射成逻辑对象,如对话框,编辑框,按钮等控件分别与抽象层中Dialog,Edit,Button等Python类相对应,测试脚本针对逻辑对象进行。抽象层降低了应用层测试脚本与功能层 GUI测试库接口的耦合性,测试脚本编写更直观。被测程序界面改变时,抽象层的存在可以大大减少测试维护的工作量,写脚本也更容易。有了抽象层,测试脚本不再需要关心底层的函数,因为抽象层中的逻辑对象会去调用底层提供的函数接口实现。

功能层包括了GUI测试库和PT代理。用户对GUI程序的操作可以分为有限几种类型,比如单双击鼠标、键盘输入字符等,因此在GAT框架中编写了一个GUI测试库来支持函数的复用,减少上层调用函数的代码量。与用户GUI程序操作相对应,它把 Windows操作系统的 API封装成通用的Mouse_RightClick等函数库。PT代理初始化时,将GetTestInfo.dll注入被测试程序,保存COM接口指针。运行过程中,PT将GetTestInfo.dll获取的窗口文件句柄和空间坐标位置信息实时传递给抽象层。

4 GA T关键技术实现

4.1 插件实现

插件主要实现注入和卸载过程、跨进程通信、获取被测试程序控件信息功能。进行GetTestInfo.dll动态库注入时,PT代理先把DLL映射到自己的进程空间,再通过调用 CreateRemoteThread实现了DLL注入与卸载,为了保证远程创建线程调用成功,PT通常还需要调用等待函数 WaitForSingleObject与被测进程同步。

测试用例脚本由脚本解释器解释运行,它运行在脚本解释器的进程空间中,而GetTestInfo.dll在被测程序的进程空间运行,因此要解决跨进程通讯问题。ATL是Microsoft Visual Studio提供的一套基于模板的 C++类库[13],利用模板类快速建立的 COM组件程序。采用 COM 接口的代理和存根模式,使得跨进程间通信就像“指针”调用,而不用考虑跨进程细节问题。GetTestInfo.dll生成COM对象实例Cget::CreateInstance(&g_pGet),CreateInstance 是CGet类的成员函数,它创建一个CGet实例,然后调用PT代理的COM服务把创建实例的指针传递给PT代理。CGet类的定义如下。

IGet是 COM接口类。接口类里的成员函数都是虚函数,CGet继承 IGet类,IGet里的成员函数在CGet中定义。

测试脚本调用PT传过来的COM接口指针获取被测程序GUI界面的信息。GetTestInfo.dll与被测试程序位于同一进程空间中,可以直接调用目标进程中的函数,从而获取被测试程序信息。GetTestInfo.dll运行的COM实例CGet继承IGet接口,因此把测试脚本需要用到的获取信息封装成 IGet的接口函数后,可以由测试脚本调用。

4.2 对象库

对象库根据界面的层次关系用 XML的层次表现出来。以图2为例,它在对象库中的代码表现形式如下。

其中,“Python 2.7.13 Setup”对话框是一个父窗口,其它控件都在这个对话框上面,所以在对象库中,这个对话框作为最外层的一个 XML节点。它属于Dialog类,所以它的class是”Dialog”类型。其它的Back按钮,Next按钮,Cancel按钮等控件都在对话框上面,所以它们是“Python 2.7.13 Setup”对话框节点的子节点。这样就形成了一个对象库。

图2 界面示例Fig.2 GUI demo

对对象库的解析过程就是Python对XML的解析过程。Python中的XML库支持解析XML[14],所以Python在解析XML前需要import xml,然后通过XML库函数实现对XML的解析。代码如下所示:

pathList = UniStr(self._path).split('-')

for i in range(len(pathList)):

if i == len(pathList)-1:self._obj=objList.GetObjectByText(objTxt, self._class)

else: self._obj = objList.GetObjectByText(objTxt)

if self._obj.HasAttr('WCtrl'):

解析模块首先会把对象的路径通过“-”把界面的层次间隔开来。抽象层解释测试脚本时,首先把路径通过“-”分隔开,然后按照XML的相应的层次关系,在 XML中一步一步深入到内层节点,找到目标节点,完成了对象库的解析过程。路径表示窗口的一个父子关系,父窗口写在前面,它的子窗口写在后面。

4.3 关键字驱动

为测试脚本采用了关键字脚本技术[15],测试脚本中的对象关键字Dialog等都由抽象层中对应的类解释。抽象层解释类时,首先做类的初始化,通过对象库唯一标识对象。当类初始化完毕,对象所有的属性,如坐标位置等,都存储在类的成员变量中。测试脚本中的对象行为由类的成员函数完成。测试脚本如下所示:

Dialog('主面板').RClickInDialog(a[6]+5,a[7]+5)Sleep(1)

Menu('设置').Select('我的信息')

Sleep(1)

Edit('搜索-条件匹配').SetValue(ATSP['UID3']['account'])

Button('主面板-打开管理器').Click()

脚本的最后一句中,“主面板-打开管理器”表示父窗口是“主面板”,“打开消息管理器”是“主面板”的一个子窗口。这句话的意思是单击“主面板”里的“打开管理器”的按钮。测试脚本执行时,抽象层首先会调用 Button的__init__成员函数对Button进行初始化。初始化函数会调用_findObj函数,这个函数通过对象库对“主面板-打开管理器”进行解析。当抽象层执行Button的Click时,Click成员函数会去调用GUI函数库里的MouseClickInsideHWnd,然后操作系统会在Button的坐标处产生鼠标点击这一虚拟事件。

5 结论

本文针对GUI测试的独特性和当前常用测试工具的一些缺点,对GUI自动化测试策略,方法和技术进行了深入的探讨。提出了新的设计,采用插件技术,关键字驱动测试脚本,对象库,GUI测试库。

使得在Windows下,测试工具的功能得到了很大的增强,脚本的编写也变得简单,编写效率也有了大大的提高。GAT全部采用脚本编写模式测试,完全了摆脱了传统捕捉/回放模式,自动化覆盖达到90%以上,而且自动化测试工具运行也变得更加稳定。目前 GAT在 GUI自动化测试解决方案的实现过程中,只是形成了一个具有简单功能的GUI自动化测试工具。实现屏幕录制,支持联机和单机工作模式,支持数据库和文件存储模式,支持多机协作将是GAT下一步研究工作的重点。

[1] 郑翠. 基于SilkTest工具对山东移动iCRM系统的自动化测试[D]. 济南: 山东大学.

[2] 王冲. Java GUI自动化测试工具的实现[D]. 上海: 东华大学.

[3] 张竞帆. 基于Selenium的一种Web自动化测试系统的设计与实现[D]. 北京: 北京交通大学.

[4] 吴琼. 基于QTP自动化测试框架的研究与应用[D]. 北京:中国科学院大学.

[5] 王健, 李亚伟, 朱璇. 使用QTP 对Silverlight 应用进行自动化测试的研究与实践[J]. 软件, 2014. 35(4): 18-20.

[6] 陈庄, 王津梁, 张醍. 手工DLL注入的检测方法研究与实现[J]. 信息安全研究. 2017, 3(3): 246-253.

[7] Jeffrey Richter. Windows核心编程(第5版)[M]. 葛子昂等译.北京: 清华大学出版社, 2008.

[8] 何卉, 徐志跃, 张秀磊. 基于COM 组件技术的测控系统软件框架设计[J]. 电子设计工程. 2017, 25(11): 61-64.

[9] 吴立金, 韩新宇, 张凯等. 一种非侵入的GUI自动化测试系统设计[J]. 计算机测量与控制.2017, 25(12): 49-53.

[10] 涂华轲, 邹华, 林荣恒. 增强的云化并行计算框架系统的设计与实现[J]. 新型工业化, 2012, 2(12): 33-40.

[11] 王影, 刘卉, 赵娟. 软件部件仿真测试平台的设计与实现[J]. 计算机工程与设计. 2017, 31(11): 3061-3065.

[12] 王如迅. 基于SWTBot技术的软件自动化测试的研究与实现[J]. 软件, 2016, 37(02): 121-128

[13] Brent Rector, Chris Sells.ATL技术内幕[M]. 北京: 科学出版社, 2003.

[14] Wesley Chun. Python核心编程(第3版)[M]. 孙波翔等译.北京: 人民邮电出版社, 2016.

[15] 党若楠, 陈健. 基于关键字驱动的Web自动化测试框架研究与实现[J]. 工业控制计算机.2017, 30(9): 46-47.

猜你喜欢
测试工具测试用例脚本
酒驾
基于SmartUnit的安全通信系统单元测试用例自动生成
Http并发连接测试工具
数据库系统shell脚本应用
基于混合遗传算法的回归测试用例集最小化研究
快乐假期
手车式真空断路器回路电阻测试电流线接头研究
福禄克推出先进的连接式测试工具系统
基于依赖结构的测试用例优先级技术
软件回归测试用例选取方法研究