UIAutomation和WPF
UIAutomation是微软从Windows Vista开始推出的一套全新UI自动化测试技术, 简称UIA。在最新的Windows SDK中,UIA和MSAA等其它支持UI自动化技术的组件放在一起发布,叫做Windows Automation API。
和前面的介绍相比,我倾向于认为UIA是一项自动化测试“技术”,而MSAA和Win32 API只是实现自动化测试的两种“方法”。这里区分“技术”和 “方法”的原因是, 一项“技术”往往有独立的模型,体贴的开发接口,用来专门解决某一类的问题,同时允许不同的实现细节。UIA可以被看作“技术”,是因为:
UIA定义了全新的、针对UI自动化的接口和模式。 分别是支持对UI元素进行遍历和条件化查询的TreeWalker/FindAll。定义了读写UI元素属性的UIA Property, 包括Name、 ID、Type、ClassName、Location、 Visibility等等。定义了UI元素行为的UIA Pattern, 比如Select、Expand、Resize、 Check、Value等等。 还引入了UIA Event接口,可以让测试程序在某些事件发生后得到通知,比如新窗口打开事件等。
以往的Win32和MSAA 设计出发点并不是为解决UI自动化。Win32旨在提供的通用开发接口, MSAA旨在提供程序的多种访问方式。相反,UIA的设计目的,以及新引入的模式和接口都完全是针对UI自动化测试的。
在后面的文章中我们会详细分析UIA的内部实现。可以看到,UIA这一套接口和模式,可以在不同平台,不同开发工具中实现和使用。其内部实现方式也因地制宜, 前后的兼容性都照顾得很好。 同时,UIA提供了托管的和非托管两种API,这些都是Win32和MSAA无法比拟的。
UIAutomtion中主要用AutomationElement 类来 表示 UI 自动化目录树中的一个 UI 自动化元素,并包含由 UI 自动化客户端应用程序用作标识符的值。
添加UIAutomation
在reference中添加UIAutomationClient,UIAutomationTypes,UIAutomationProvider 和UIAutomationClientsideProviders。
(Donet 3.0以上的项目中才有)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Automation;
using System.Windows;
namespace ConsoleApplication2
{
class Cal
{
AutomationElement calcWindow = null;
string resultTextAutoId = "150";
string btn5AutoId = "135";
string btn3 = "133";
string btn2 = "132";
string btnPlus = "93";
string btnSub = "94";
string btnEqual = "121";
static void Main(string[] args)
{
Cal autoClient = new Cal();
//创建新窗口打开事件的回调,只有等计算器窗口打开后,测试开始执行
AutomationEventHandler eventHandler = new AutomationEventHandler(autoClient.OnWindowOpenOrClose);
//把事件挂接在桌面根元素,开始监听
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, eventHandler);
//启动计算器程序,当计算器窗口打开后,新窗口事件得到触发
System.Diagnostics.Process.Start("calc.exe");
Console.ReadLine();
}
void OnWindowOpenOrClose(object src, AutomationEventArgs e)
{
if (e.EventId != WindowPattern.WindowOpenedEvent)
{
return;
}
AutomationElement sourceElement;
try
{
sourceElement = src as AutomationElement;
//监察印发新窗口打开事件的窗口是否是计算器
//在正式代码中,为了支持本地化测试,字符串应该从资源文件中读取
if (sourceElement.Current.Name == "计算器")
{
calcWindow = sourceElement;
}
}
catch (ElementNotAvailableException)
{
return;
}
//开始执行测试
ExecuteTest();
}
private void ExecuteTest()
{
//执行3+5-2
//调用函数执行按钮点击
ExecuteButtonInvoke(btn3);
ExecuteButtonInvoke(btnPlus);
ExecuteButtonInvoke(btn5AutoId);
ExecuteButtonInvoke(btnSub);
ExecuteButtonInvoke(btn2);
ExecuteButtonInvoke(btnEqual);
//调用getcurrentresult函数,获取计算器输出窗口的值
if (GetCurrentResult() == "6")
{
Console.WriteLine("execute pass!");
return;
}
Console.WriteLine("execute faile");
}
//调用函数执行按钮点击
private void ExecuteButtonInvoke(string automationId)
{
//创建捕获按钮条件。该条件中有两个判断
//分别判断automationid是否为指定字符串
//以及控件类型是否为按钮
System.Windows.Automation.Condition conditions = new AndCondition(new PropertyCondition(AutomationElement.AutomationIdProperty,automationId),
new PropertyCondition(AutomationElement.ControlTypeProperty,ControlType.Button));
AutomationElement btn = calcWindow.FindFirst(TreeScope.Descendants,conditions);
//获取按钮的invokepattern接口
InvokePattern invokeptn = (InvokePattern)btn.GetCurrentPattern(InvokePattern.Pattern);
//调用invoke接口,完成按钮点击
invokeptn.Invoke();
}
private string GetCurrentResult()
{
System.Windows.Automation.Condition conditions =
new AndCondition(new PropertyCondition(AutomationElement.AutomationIdProperty,resultTextAutoId),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Text));
AutomationElement btn = calcWindow.FindFirst(TreeScope.Descendants, conditions);
//得去text控件的名字,改名字就是输出字符串
return btn.Current.Name;
}
}
}