背景
Windows客户端产品基于UI的自动化测试自古以来都比较难做,基于非标准控件的UI自动化更是难上加难。进程间基于UI的自动化测试会对产品UI布局有很大依赖,产品布局的每一次改动都可能会使相关自动化测试用例失效。
于是我们需要一种可以动态跟随UI变化的机制,可以在运行时确定UI的布局,进而实施相应UI自动化测试。除了获取UI布局外,像UI的一些状态信息(位置、文字等),对自动化测试也是非常必要。为此,我们采用代理的方式,以“假注入”的形式实现了对主程序运行时状态的获取。
概述
被测产品的Proxy是由运行于产品进程当内的命令处理模块(ProxyAgent)和运行于进程外的适配器模块(ProxyAdapter)组成的。ProxyAgent模块运行于产品进程内可以带来很多便利,一是上面提到的可以动态获取产品的状态信息,二是增加了测试人员的主动权,因为测试代码与产品代码同源,许多操作不需要再请求开发人员来实现,测试人员自己可以安排人员去实现。ProxyAdapter从名字可以看出为适配器层,它屏蔽了C++的复杂接口,为上层调用者提供了标准的Python接口。
Proxy结构
被测产品在代码层面进行了支持,通过加载动态链接库的方式实现。整个Proxy由两部分组成:产品内代理层和产品外协议适配器层。
※ 产品内代理层(ProxyAgent)。产品进程内DLL,它接收命令、查询并返回产品UI的状态信息。
※产品外协议适配器层(ProxyAdapter)。转发用户命令、接收进程内ProxyAgent返回信息并提供Python接口,方便上层自动化库调用。


 


图1 Proxy在自动化库中的结构


实现机制
Proxy为产品进程内动态链接器,与产品工程同源,可以方便地引用产品当中的各种库和头文件,相当于产品的测试后门,理论上讲ProxyAgent可以协助测试发起任何产品支持的动作。ProxyAdapter为独立的适配器层,可独立编译,它向上为python提供接口,中间将命令信息进行转化,下层与进程外的Proxy打交道,进行信息交换。ProxyAgent与ProxyAdaper的出现使测试代码可以直接获取运行中产品的信息,大大提高了系统级测试的效率。增加Proxy前后示意图如图2所示


 


图2 无代理VS有代理


ProxyAgent实现机制
ProxyAgent实质上是一个进程内DLL,此DLL提供初始化接口和反初始化接口,开发人员判断指定的DLL是否存在,若存在则调用此DLL的初始化接口,产品退出时调用反初始化接口。产品当中,初始化时ProxyAgent建立隐藏窗体,用来接收运行中发过来的消息;反初始时销毁隐藏窗体。流程如图3


 


图3 ProxyAgent生命周期


ProxyAdapter实现机制
本模块通过Windows消息和共享内存来传递命令,与产品当中的ProxyAgent实现交互。产品状态信息通过共享内存实现。在本层进行了封装,将调用接口封装成python接口,方便上层python关键字库调用,可以降低用例编写成本。
模块设计及实现
ProxyAdapter需要接收上层的请求,并将请求转发至ProxyAgent层。因此需要定义相应的协议来进行通信。产品状态信息有多种,相应的对象也有多种,不同的方式导致无法提供唯一接口,现阶段存在两种接口。通过这些定义好的协议和接口,可以实现一个完整的通信过程。通信过程如图四所示。


 



图4 Proxy调用过程


通信协议
Proxy接收用户命令,需要获取产品上某个控件的状态信息。通常情况下需要两个标识才能实现。具体表现为<对象标识,控件标识>。由于产品设计原因,现存在两种协议。我们采用Windows消息和共享内存实现命令的传输,所以下面协议的表达形式按SendMessage参数来进行。两种协议对不同的UI控件实现支持。
 


图5 协议一
 


图6 协议二
通信接口
存在两种协议,所以模块提供了两种类型的接口。接口1形式为GetXXXXInfoByIdx,每个接口对应一个真实存在的产品窗体对象;接口2形式更为通用,它只有一个接口,通过参数来标识要获取信息的对象,形如GetUIInfoByName(int nObjectID,CString szName,CString szType);
ProxyAgent代码结构
ProxyAgent在接收到命令之后会根据其目标对象将命令转发至相应的处理类(函数),通过调用统一的接口,获取控件最基本的状态信息。当有新窗口加入时,只需要实现相应的子类即可。表1展示了获取某个功能窗口相应控件信息的过程。可以看出,在代码中添加新功能较为简单。
表1 增加某些新功能的支持
 



ProxyAdapter代码结构
ProxyAdapter结构较为简单,实现简单的数据转化、转发功能。一是把上层数据转化为Windows消息和共享内存数据发送到ProxyAgent,二是从共享内存接收ProxyAgent返回的数据。表2展示了一个典型的数据转发过程。
 



Proxy的优势
Google在输入法测试和产品测试当中均使用了代理模式,它为自动化测试提供了非常强大的支持。系统测试多是在产品进程外部的操作,受限于进程隔离和Windows界面,很多操作非常难实现,所以才有了诸多前辈们在Windows上的自动化尝试。常见的Windows自动化操作在这里就不一一列举,感觉兴趣的同学可以百度之。总体上我们可以看,每个方法都在努力与被测产品建立联系。通过添加进程内代理,可以使艰难的进程间操作变得简单。具体优势如下:
※ 直接获取被测产品的状态
※ 绕过复杂的表层,直接测试关键功能
※ QA对被测产品能有更大的控制权
Proxy的应用效果
Proxy模块已成功应用到产品自动化测试当中,包括所有的UI自动化工作、持续集成的Slow Test、Quick Test,Proxy的应用为产品自动化扩展测试范围、增加稳定性提供了良好的基础。

 

(作者:szfeng)