说明:以下内容是根据2010年我在公司的一次关于UI Automation的Workshop上的PPT整理而来,在公司我和几位同事基于UI Automation开发了一款非常强大的UI自动化测试工具,此工具已经在公司得到广泛运用(有十几个产品采用),用于替代昂贵的、且脚本很难维护的商用软件。此文只探讨UIA相关的一些通用技术和一点点UI自动化测试工具的设计经验,因为保密的需要,涉及到公司的这款测试工具部分此文不做介绍。
在没有MSUIA(Microsoft UI Automation,以下简称UIA)之前,大家只能通过MSAA(Microsoft Active Accessibility)、Win32 API等Native的方式来操控Windows控件,要想自己写一个UI自动化测试工具是很难很难的,因此这一领域一直被大厂商所垄断,价格也贵得惊人,不是大的软件公司也是用不起这些工具的(除非盗版),比如QTP、Robot、SilkTest等商用工具。UIA定义了一套全新的、针对UI自动化的接口和模式,以往的Win32和MSAA设计出发点并不是为解决UI自动化(Win32旨在提供的通用开发接口,MSAA技术的初衷则是为了方便残疾人使用Windows 程序),而UIA的设计目的(微软也需要一套技术、工具来自动化测试自己的产品)、以及新引入的模式和接口都完全是针对UI自动化测试的。UIA的出现,让草根UI自动化测试工具成为一种可能,看完本文你若有这样的需求就赶紧自己造一个吧:)
在继续介绍UIA之前,大家需要先熟悉UIA的几个名词术语。
在UIA里,每一个UI控件即是一个Automation Element,所有的Elements是存储在一个树状结构中的,Windows的桌面是这颗树的根结点(RootElement)。
UIA tree就是上面所指的树,每个Application都可以看作是一棵子树。
Element都有一些属性(Properties),比如Automation ID、Name、ControlType等,要找到一个控件主要是通过这些的属性来查找的。
在找到一个控件后,对一个控件进行操控的时候就需要用到这个控件支持的控制模式(Control patterns)了,比如:一个TextBox的ValuePattern可以用来获取TextBox里面的文字。
当UI控件有什么变动的时候,可能会触发一些事件(比如:弹出HelpText),如果有Client订阅了这个事件则会收到事件的通知。
上图左边那部分叫Client,本文指的是UI自动化测试工具,右边叫Application,指的是被测运用程序。Client和Application在启动的时候都会载入UI Automation Core(UIAutomationCore.dll),UI自动化测试工具就是通过UI Automation Core来操控运用程序的。从这个架构图上也可以看到UIA封装了一些MSAA和Win32的接口、屏蔽了Win32和.NET运用程序的差异,UI Automation Core对外提供了统一的接口,这也就大大简化了Client这边的实现。
基于UIA这套体系实现UI自动化需要解决的两个核心的问题,一是控件查找(Find Controls);二是控件操作(Control manipulation),下面对此分别进行介绍。
UIA除了提供了最基本的遍历整个UIA tree的API(TreeWalker)外,另外也提供了一些Build-in的控件查找API。
TreeWalker是标准的对UIA tree的遍历API,它是一切控件查找API的基础。
UIA自带的这些Build-in的查找API是基于TreeScope和Filting Condition的,查找控件需要定义一个查找范围和过滤条件。
下面是一个最简单的示例,在RootElement桌面下查找子结点里Name属性是“Simple App”的一个控件,因为是在桌面的子结点(一级),它会是一个Application:
Condition winNameCond = new PropertyCondition(AutomationElement.NameProperty, "Simple App"); AutomationElement app = AutomationElement.RootElement.FindFirst(TreeScope.Children, winNameCond)
通过上面的Find Controls找到一个控件后,接下来就是如何操作它实现UI自动化的问题了,比如:点击一个Button,选择一个TreeViewItem等等。下面是一个已做好封装的UITextBox这个控件类里获取TextBox的文字的参考实现:
public virtual string GetText() { object o = new object(); if (this.Element.TryGetCurrentPattern(ValuePattern.Pattern, out o)) { ValuePattern pattern = o as ValuePattern; return pattern.Current.Value; } else { return ""; } }
如上面的代码所示,控件操作有一个固定的模式,首先尝试获取控件的Control Pattern,如果控件存在这样的Control Pattern就通过这个Pattern来操控控件,如果没有则需要自定义这个操作或抛出异常等。
直接基于UIA来实现UI自动化不是不可以,但没有人会这么蛮干。为了获取一个Textbox里的文字这样简单的事情,得到处拷贝上面那段的代码,这是不可取的。所以针对上面说的两个核心的问题,我们需要在UIA上进行一些简化、封装(当然光有这两点是不够的,比如还需要提供一些Native的支持、UI同步、Log等功能,本文对此不做讨论):
封装常用控件的基本操作,如:Button的Click、TreeView的Expand和Collapse、Datagrid的操作等等,使控件的操作变成一个简单的API调用,可以考虑把UIA ControlType里定义的30几种控件中最常用的控件基本操作都实现了,这样写测试脚本的时候就很安逸了。一些产品中可能会使用一些非标的控件,对于这些自定义的控件,用标准控件的操控API可能不管用,通常通过下面两种方式来解决这个问题,一是继承标准控件,重写操控API的实现,如果能够实现的话;二是让开发人员改代码,尽量不要使用非标控件:)
当有了应手的测试工具后,测试脚本的维护依然是个老大难的问题,这个需要有一个好的自动化测试framework来隔离UI的变化,尽量减少维护成本,一个好的framework大体需要有下面几级的分层:
总之,通过UIA实现一套自己的强大的UI自动化测试工具不是不可能,希望本文对一些从事UI自动化测试的同学有所帮助。
扩展资料:
转载:http://feilong.me/2011/01/ms-ui-automation-tool-design-guide