2014-09-17
UI Automation 就是用另一个程序来控制UI 程序,模拟用户操作。他包含三步骤:
微软UI Automation技术提供了很好的实现模型。简单来讲,它就是几个dll,提供了一套API及其相应的模式,让软件的开发者遵循该模式去实现相应的interface,从而使测试人员更方便的编写UI Automation。
MS UIA明确定义了两个role:UIA Provider即软件本身,也可称为服务器端,UIA Client即自动化脚本和相关的assistive technology applications,见图1。
图1 架构简易图
图2 官方架构图
在图2中,Applications是软件本身,也可成为服务器端,Accessibility Tool是客户端,即测试软件或测试工具(如UISpy.exe)。
从图2可得知UIAutomation Core是通信基础代码,而且Applications和Accessibility Tool是通过管道通信的。
UIA主要有4个组件:
组件 |
描述 |
---|---|
提供程序 API(UIAutomationProvider.dll 和 UIAutomationTypes.dll) |
定义了各种行为的interface,例如,假设有个自定义的控件,开发人员觉得它需要支持Dock行为,就需要实现IDockProvider接口。 |
客户端 API(UIAutomationClient.dll 和 UIAutomationTypes.dll) |
定义了各种控件模式,以及一些用来支持更好的定位控件的辅助条件搜索类 |
UiAutomationCore.dll |
处理提供程序与客户端之间的通信的基础代码(有时也称为 UI 自动化核心)。 |
UIAutomationClientsideProviders.dll |
一组用于标准旧版本控件的 UI 自动化提供程序。(WPF 控件为 UI 自动化提供本机支持。)此支持自动提供给客户端应用程序。 |
图3 UIA dll 使用
常用命名空间:
namespace |
引用的 DLL |
读者 |
---|---|---|
UIAutomationClient;UIAutomationTypes |
UI 自动化客户端开发人员;用于查找 AutomationElement 对象、注册 UI 自动化事件以及与 UI 自动化控件模式一起使用。 |
|
UIAutomationProvider;UIAutomationTypes |
除 WPF 之外的框架的 UI 自动化提供程序开发人员。 |
|
UIAutomationClient;UIAutomationTypes |
除 WPF 之外的框架的 UI 自动化提供程序开发人员;用于实现 TextPattern 控件模式。 |
|
PresentationFramework |
WPF 的 UI 自动化提供程序开发人员。 |
UI 自动化将 UI 的每一部分作为一个 AutomationElement 向客户端应用程序公开。
元素包含在树结构中,以桌面作为根元素。
AutomationElement 对象公开它们所表示的 UI 元素的通用属性。 其中一个属性是控件类型,它将其基本外观和功能定义为一个可识别的实体:例如按钮或复选框。
此外,元素还公开控件模式,以提供特定于这些元素的控件类型的属性。 控件模式还公开方法,使客户端能够获取有关元素的进一步信息并提供输入。
注意:控件类型和控件模式之间并不是一一对应的关系。 多个控件类型可以支持同一个控件模式,一个控件可以支持多个控件模式,每个控件模式公开其行为的不同方面。 例如,一个组合框至少具有两个控件模式:一个表示其展开和折叠功能,另一个表示选择机制
UI 自动化还通过事件向客户端应用程序提供信息。 与 WinEvent 不同的是,UI 自动化事件并不基于广播机制。 UI 自动化客户端注册特定的事件通知,并且可以请求将特定的 UI 自动化属性和控件模式信息传入其事件处理程序中。 此外,UI 自动化 事件包含到引发该事件的元素的引用。 提供程序可以通过有选择地引发事件来改善性能,具体取决于所有客户端是否在侦听。
在UIA中,程序UI的每一个部分都被认为是一个AutomationElement类,他们是一个树状的结构,Desktop被认为是每个windows based app的UIA树状图的根,从类的定义中,我们也可以看到一个AutomationElement类中有一个static的RootElement属性。
该树的结构中,一共有3中View Model,分别为Raw View, Control View和Content View:
Raw View提供的信息最多,也是其他view的基础,最贴近于程序本身的编程结构;
Control View是Raw View的子集,它最贴近于最终用户所能感知的UI结构,但是它不包含不能和用户相互交互的一些UI,例如listview的header,toolbar等等;
Content View则是Control View的一个子集,它只包含能和用户直接交互真实信息的控件,比如接受键盘输入的Textbox,选择不同值的Combobox;而诸如lable等控件则不会包含在其中。
控件模式需实现定义控件中可用的一项独立功能所需的方法、属性、事件和关系:
UIA大概一共定义了38种pattern,代表了常用的控件行为,他们也会提供一些具体的功能性的属性。
如某个控件需要有InvokePattern,则provider和client相对应的则为:
控件模式类 (Client) |
提供程序接口(Provider) |
说明 |
---|---|---|
InvokePattern |
IInvokeProvider |
用于可被调用的控件,如按钮。 |
每个property都由一个数字和名字来标识,provider用数字ID来确定属性请求(出于安全原因,UI 自动化提供程序将从 Uiautomationtypes.dll 中包含的一组单独的类中获取这些对象。);而client则用AutomationProperty类获取具体的某一属性的内容。
UIA是采用订阅模型,而不是以前的广播事件模型。定义了四种事件类型:Property change,Element action,Structure change和Global desktop change。
下面这个示例用测试程序完成以下操作:
1 //Reference UIAutomationClient and UIAutomationTypes 2 3 using System; 4 using System.Windows.Automation; 5 using System.Windows; 6 7 namespace CalcClient 8 { 9 class CalcAutomationClient 10 { 11 AutomationElement calcWindow = null; //Main UI Window element 12 //The following ID can be obtained from tool: UI Spy 13 string resultTextAutoID = "150"; //ID for Text element of output window 14 string btn5AutoID = "135"; //ID for button 5 15 string btn3AutoID = "133"; // ID for button 3 16 string btn2AutoID = "132"; // ID for button 2 17 string btnPlusAutoID = "93"; // ID for button + 18 string btnSubAutoID = "94"; // ID for button - 19 string btnEqualAutoID = "121"; // ID for button = 20 21 static void Main(string[] args) 22 { 23 CalcAutomationClient autoClient = new CalcAutomationClient(); 24 25 //Create callback for new Window open event. Test should run only when the main Window shows. 26 AutomationEventHandler eventHandler = new AutomationEventHandler(autoClient.OnWindowOpenOrClose); 27 //Attach the event with desktop element and start listening. 28 Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, eventHandler); 29 30 //Start caculator. When new window opens, the new window open event should fire. 31 System.Diagnostics.Process.Start("calc.exe"); 32 33 //Wait execution 34 Console.ReadLine(); 35 } 36 37 void OnWindowOpenOrClose(object src, AutomationEventArgs e) 38 { 39 if (e.EventId != WindowPattern.WindowOpenedEvent) 40 { 41 return; 42 } 43 44 AutomationElement sourceElement; 45 46 try 47 { 48 sourceElement = src as AutomationElement; 49 50 //Check the event source is caculator or not. 51 //In production code, string should be read from resource to support localization testing. 52 if (sourceElement.Current.Name == "Calculator") 53 { 54 calcWindow = sourceElement; 55 } 56 } 57 catch (ElementNotAvailableException) 58 { 59 return; 60 } 61 62 //Start testing 63 ExecuteTest(); 64 } 65 66 void ExecuteTest() 67 { 68 //Execute 3+5-2 69 //Invoke ExecuteButtonInvoke function to click buttons 70 ExecuteButtonInvoke(btn3AutoID); 71 ExecuteButtonInvoke(btnPlusAutoID); 72 ExecuteButtonInvoke(btn5AutoID); 73 ExecuteButtonInvoke(btnSubAutoID); 74 ExecuteButtonInvoke(btn2AutoID); 75 System.Threading.Thread.Sleep(1000); 76 ExecuteButtonInvoke(btnEqualAutoID); 77 78 //Invoke GetCurrentResult function to read caculator output 79 if (GetCurrentResult() == "6") 80 { 81 Console.WriteLine("Execute Pass!"); 82 return; 83 } 84 85 Console.WriteLine("Execute Fail!"); 86 } 87 88 void ExecuteButtonInvoke(string automationID) 89 { 90 91 //Create query condition object, there are two conditions. 92 //1. Check AutomationID 93 //2. Check Control Type 94 Condition conditions = new AndCondition( 95 new PropertyCondition(AutomationElement.AutomationIdProperty, automationID), 96 new PropertyCondition(AutomationElement.ControlTypeProperty, 97 ControlType.Button)); 98 99 AutomationElement btn = calcWindow.FindAll(TreeScope.Descendants, conditions)[0]; 100 101 //Obtain the InvokePattern interface 102 InvokePattern invokeptn = (InvokePattern)btn.GetCurrentPattern(InvokePattern.Pattern); 103 104 //Click button by Invoke interface 105 invokeptn.Invoke(); 106 } 107 108 string GetCurrentResult() 109 { 110 111 Condition conditions = new AndCondition( 112 new PropertyCondition(AutomationElement.AutomationIdProperty, resultTextAutoID), 113 new PropertyCondition(AutomationElement.ControlTypeProperty, 114 ControlType.Text)); 115 116 AutomationElement btn = calcWindow.FindAll(TreeScope.Descendants, conditions)[0]; 117 118 //Read name property of Text control. The name property is the output. 119 return btn.Current.Name; 120 } 121 } 122 }
UISpy可以当作Client,找到Server所提供的属性、控件模式,也可对Server进行模拟操作。
图4 UISpy attached to Calculator
在UIA架构中提到:对于标准控件而言,默认是支持UIA的,而对于自定义的控件,需要实现该控件的行为对应于UIA所定义的interface。
这里提到的实现UIA所定义的interface,就是UIA provider。UIA provider可在根据实际情况在服务器端和客户端实现,示例如下:
客户端:
Client-Side UI Automation Provider - WinForm Sample
服务器端:
Server-Side UI Automation Provider - WinForm Sample
Server-Side UI Automation Provider - WPF Sample
提倡第一种方法。
Avoid localize issue, read resource string
前面提到了UIA作为全新UI自动化测试技术的优势,但这并不能解决所有的UI 自动化问题。 自动化框架正是为了自动化技术没有完全解决的问题。比如:
所以,单纯的某个自动化技术或者方法也无法满足需求。为了解决上述问题,各种自动化测试框架逐渐涌现和发展。微软内部有多个不同的自动化框架,设计理念和侧重点各有不同。 Visual Studio 2010将加入对自动化测试的支持。 在CodePlex上面, 也可以找到多种框架,比如White和UI Automation Verify。
[2] UI 自动化概述
[3] UI 自动化树概述
[4] UI 自动化控件模式概述
[5] UI 自动化属性概述
[6] UI 自动化事件概述
[7] 使用 UI 自动化进行自动化测试