您的位置:首页 > 产品设计 > UI/UE

MS UI Automation Introduction

2014-09-18 12:59 288 查看

MS UI Automation Introduction

2014-09-17

MS UI Automation是什么

UIA架构

UI自动化模型

UI自动化树概述

UI自动化控件模式概述

UI 自动化属性概述

UI 自动化事件概述

示例

使用UISpy工具

UI自动化提供者

常见问题分析解决

控件无法识别

Timing issue

本地化问题

自动化技术和自动化框架

参考

MS UI Automation是什么[1]

返回

UI Automation 就是用另一个程序来控制UI 程序,模拟用户操作。他包含三步骤:

找到UI 元素

模拟用户操作

检查UI属性和行为

微软UI Automation技术提供了很好的实现模型。简单来讲,它就是几个dll,提供了一套API及其相应的模式,让软件的开发者遵循该模式去实现相应的interface,从而使测试人员更方便的编写UI Automation。

UIA架构[1]

返回

MS UIA明确定义了两个role:UIA Provider即软件本身,也可称为服务器端,UIA Client即自动化脚本和相关的assistive technology applications,见图1。

UIA Provider: 开发人员确定控件行为并实现对应的UIA control pattern(注意:对于标准控件而言,默认是支持UIA的,而对于自定义的控件,需要实现该控件的行为对应于UIA所定义的interface。)

UIA Client:相对而言,UIA Client则简单了很多,只需调用相关的UIA API去完成自动化测试脚本。

//Reference UIAutomationClient and UIAutomationTypes

using System;
using System.Windows.Automation;
using System.Windows;

namespace CalcClient
{
class CalcAutomationClient
{
AutomationElement calcWindow = null; //Main UI Window element
//The following ID can be obtained from tool: UI Spy
string resultTextAutoID = "150"; //ID for Text element of output window
string btn5AutoID = "135"; //ID for button 5
string btn3AutoID = "133"; // ID for button 3
string btn2AutoID = "132"; // ID for button 2
string btnPlusAutoID = "93"; // ID for button +
string btnSubAutoID = "94"; // ID for button -
string btnEqualAutoID = "121"; // ID for button =

static void Main(string[] args)
{
CalcAutomationClient autoClient = new CalcAutomationClient();

//Create callback for new Window open event. Test should run only when the main Window shows.
AutomationEventHandler eventHandler = new AutomationEventHandler(autoClient.OnWindowOpenOrClose);
//Attach the event with desktop element and start listening.
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement, TreeScope.Children, eventHandler);

//Start caculator. When new window opens, the new window open event should fire.
System.Diagnostics.Process.Start("calc.exe");

//Wait execution
Console.ReadLine();
}

void OnWindowOpenOrClose(object src, AutomationEventArgs e)
{
if (e.EventId != WindowPattern.WindowOpenedEvent)
{
return;
}

AutomationElement sourceElement;

try
{
sourceElement = src as AutomationElement;

//Check the event source is caculator or not.
//In production code, string should be read from resource to support localization testing.
if (sourceElement.Current.Name == "Calculator")
{
calcWindow = sourceElement;
}
}
catch (ElementNotAvailableException)
{
return;
}

//Start testing
ExecuteTest();
}

void ExecuteTest()
{
//Execute 3+5-2
//Invoke ExecuteButtonInvoke function to click buttons
ExecuteButtonInvoke(btn3AutoID);
ExecuteButtonInvoke(btnPlusAutoID);
ExecuteButtonInvoke(btn5AutoID);
ExecuteButtonInvoke(btnSubAutoID);
ExecuteButtonInvoke(btn2AutoID);
System.Threading.Thread.Sleep(1000);
ExecuteButtonInvoke(btnEqualAutoID);

//Invoke GetCurrentResult function to read caculator output
if (GetCurrentResult() == "6")
{
Console.WriteLine("Execute Pass!");
return;
}

Console.WriteLine("Execute Fail!");
}

void ExecuteButtonInvoke(string automationID)
{

//Create query condition object, there are two conditions.
//1. Check AutomationID
//2. Check Control Type
Condition conditions = new AndCondition(
new PropertyCondition(AutomationElement.AutomationIdProperty, automationID),
new PropertyCondition(AutomationElement.ControlTypeProperty,
ControlType.Button));

AutomationElement btn = calcWindow.FindAll(TreeScope.Descendants, conditions)[0];

//Obtain the InvokePattern interface
InvokePattern invokeptn = (InvokePattern)btn.GetCurrentPattern(InvokePattern.Pattern);

//Click button by Invoke interface
invokeptn.Invoke();
}

string GetCurrentResult()
{

Condition conditions = new AndCondition(
new PropertyCondition(AutomationElement.AutomationIdProperty, resultTextAutoID),
new PropertyCondition(AutomationElement.ControlTypeProperty,
ControlType.Text));

AutomationElement btn = calcWindow.FindAll(TreeScope.Descendants, conditions)[0];

//Read name property of Text control. The name property is the output.
return btn.Current.Name;
}
}
}


View Code

使用UISpy工具

UISpy可以当作Client,找到Server所提供的属性、控件模式,也可对Server进行模拟操作。



图4 UISpy attached to Calculator

UI自动化提供者

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

常见问题分析解决[8]

返回

控件无法识别

如果是因为自动化测试工具的限制,比如对于WinForm的控件,有些自动化工具就不能识别,碰到这种情况,最好是看这个工具有没有扩展可以用,比如Silktest的.Net Framework扩展。如果不行,那只能换自动化测试工具了。所以这个凸显出在做自动化测试以前,选择自动化测试工具的重要性。

如果是因为控件比较复杂,自动化工具可以识别,但是无法操作。这时我们可以通过Window API以及消息的方式来做,比如自己去调Window API来操作窗口,或者请开发实现一下消息的接口来给自动化工具调用等

跟开发沟通,让他们的控件支持IAccessible接口,然后我们通过MSAA来操作(如果是WPF控件,则需要实现UIAutomation定义的一些接口)。不过一般情况下,除了微软这样对软件的Accessible要求很高的公司,其它公司很少会花费时间来实现这个接口……。 另外扯一句,产品的Accessible的程度,实质上决定了一个公司能对产品做自动化测试的程度。

如果以上方法都不行,那只有最后一个双刃剑可以用了,就是鼠标键盘模拟。理论上来说,只要用户可以操作的东西,只要有界面,就可以通过鼠标键盘模拟来实现(君不见N多游戏外挂的键盘鼠标模拟大法)。就如双刃剑一样,这种做法是杀敌一千,自损八百。因为鼠标键盘模拟非常依赖于当前激活的窗口以及光标位置和焦点位置,而且同步起来很困难。这也造成了后期维护成本很高。

Timing issue

提倡第一种方法。

Waiter/EventDriven

Retry sleep a small interval value

Thread.Sleep a long time

本地化问题

Avoid localize issue, read resource string

自动化技术和自动化框架[8]

返回

前面提到了UIA作为全新UI自动化测试技术的优势,但这并不能解决所有的UI 自动化问题。 自动化框架正是为了自动化技术没有完全解决的问题。比如:

自动化中的同步和等待。 对于稍复杂的UI 程序,测试程序往往需要根据测试目标的状态决定 下一步的操作。 比如测试文件另存为功能的时候,若保存路径是网络路径,可能会因为网络延迟导致整个UI停顿比较长的时间。这个时候测试,程序如果不顾当前状态而简单地执行下一步操作,比如新建文件, 很可能会因为UI延迟而失败。 正确的做法是,测试程序应该等待文件保存成功返回后,再进行下一步操作。 这就是自动化中同步和等待的一个例子。实现同步和等待有多种方法,最简单粗暴的做法是硬编码一个长时间的 Sleep在测试代码中。 稍微好一点的做法可以采取小时间片的轮询状态检查, 或者反复重试。 借助 UIA的Event Pattern,可以尝试捕获另存为窗口的关闭WindowClosedEvent。 如果要做得完善一点, 可以把多种方法结合, 另外再额外检查目标程序的CPU使用情况,消息循环是否有回应,设定超时时间等等。

冗繁的编码过程。 对于一个UI窗口,里面可能有几十个子控件或者子窗口。 在编写测试代码的时候, 如果对这些子元素的获取,操作不能简化, 势必导致代码冗繁,难以维护。 借助自动代码生成和ORM (Object Role Modeling)等技术, 可以解决这个问题。 比如可以用工具把窗口及其子元素的关系和搜索条件都序列化到XML文件中, 然后采用ORM技术即可在代码中轻松获取子元素。

多语言和本地化测试。多语言和本地化的测试对UI来说显得尤为重要。 UI程序往往通过资源文件来定义所显示的内容, 这就要求自动化测试要可以方便读取和定位程序的资源文件, 来支持多语言和本地化测试。

支持工具和辅助函数的匮乏。 对于大的项目研发, 通过好的工具来减小开发成本是非常必要的。 就UI自动化来说, 如果自动化测试用例可以通过一次录制,多次播放来做的话,成本会减少很多。 在VS2010中就提供了这样的录制-播放功能。 详细视频可以参考How to create record and playback Test Cases in Visual Studio Beta2。

区分功能性测试和用户真实行为模拟。 前面提到, 就点击按钮功能来说, 可以通过SendKey来模拟鼠标操作, 或者通过Windows Message来直接触发点击事件。 这两种不同方法各有优劣。 比如当按钮被其它元素遮挡, 通过SendKey进行模拟就会导致失败,而直接发送Windows Message还是会成功。 孰优孰劣取决于要达到的目的。 如果单纯为了测试按钮点击后导致的结果,通过Windows Message来模拟就省去了很多麻烦。 相反, 如果是界面测试, 通过SendKey来模拟就可以让按钮被遮挡的bug暴露出来, 而Windows Message则不能发现这样的问题。

所以,单纯的某个自动化技术或者方法也无法满足需求。为了解决上述问题,各种自动化测试框架逐渐涌现和发展。微软内部有多个不同的自动化框架,设计理念和侧重点各有不同。 Visual Studio 2010将加入对自动化测试的支持。 在CodePlex上面, 也可以找到多种框架,比如White和UI Automation Verify。

参考

[1] MS UI Automation简介

[2] UI 自动化概述

[3] UI 自动化树概述

[4] UI 自动化控件模式概述

[5] UI 自动化属性概述

[6] UI 自动化事件概述

[7] 使用 UI 自动化进行自动化测试

[8] UI Automation - under the hood
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: