UIAutomation 自动化
Introduction
UI Automation是Microsoft .NET 3.0框架下提供的一种用于自动化测试的技术,是在MSAA基础上建立的,MSAA就是Microsoft Active Accessibility。UI Automation在某些方面超过了MSAA,UI自动化提供了Windows Vista中,微软Windows XP的全部功能,和Windows Server 2003。
在UI Automation中,所有的窗体、控件都表现为一个AutomationElement, AutomationElement 中包含此控件或窗体的属性,在实现自动化的过程中,我们通过其相关属性进行对控件自动化操作。对于UI用户界面来说,所有显示在桌面上的UI,其实际是一个UI Tree,根节点是desktop。我们可以使用UI Spy或者是SPY++来获得Window和Control的相关信息。在UI Automation里,根节点表示为AutomationElemnet.RootElement. 通过根节点,我们可以通过窗体或控件的Process Id、Process Name或者Window Name找到相应的子AutomationElement,例如Dialog、Button、TextBox、Checkbox等标准控件,通过控件所对应的Pattern进行相关的操作。
UI Automation structure
如下图所示:

1. 在服务端由UIAutomationProvider.dll和UIAutomationTypes.dll提供。
2. 在客户端由UIAutomationClient.dll和UIAutomationTypes.dll提供。
3. UIAutomationCore.dll为UI自动化的核心部分,负责Server端和Client端的交互。
4. UIAUtomationClientSideProvides.dll为客户端程序提供自动化支持。
使用UI Automation实现自动化测试--2
本文通过一个实例来介绍怎样使用UI Automation实现软件的自动化测试。
1. 首先建立一个待测试的winform程序,即UI Automation的服务端。
下面是button事件处理程序。
private void button1_Click(object sender, EventArgs e)
{
int i = int.Parse(textBox1.Text);
int j = int.Parse(textBox2.Text);
textBox3.Text = (i + j).ToString();
}
2. 建立一个测试程序,做UI Automaion的客户端。
添加引用:UIAutomationClient.dll 和 UIAutomationTypes.dll
using System;
2
using System.Diagnostics;3
using System.Threading;4
using System.Windows.Automation.Provider;5
using System.Windows.Automation.Text;6
using System.Windows.Automation;7

8
namespace UIAutomationTest9
{10
class Program11
{12
static void Main(string[] args)13
{14
try15
{16
Console.WriteLine("\nBegin WinForm UIAutomation test run\n");17
// launch Form1 application18
// get refernce to main Form control19
// get references to user controls20
// manipulate application21
// check resulting state and determine pass/fail22

23
Console.WriteLine("\nBegin WinForm UIAutomation test run\n");24
Console.WriteLine("Launching WinFormTest application");25
//启动被测试的程序26
Process p = Process.Start(@"E:\Project\WinFormTest\WinFormTest\bin\Debug\WinFormTest.exe");27

28
//自动化根元素29
AutomationElement aeDeskTop = AutomationElement.RootElement;30

31
Thread.Sleep(2000);32
AutomationElement aeForm = AutomationElement.FromHandle(p.MainWindowHandle);33
//获得对主窗体对象的引用,该对象实际上就是 Form1 应用程序(方法一)34
//if (null == aeForm)35
//{36
// Console.WriteLine("Can not find the WinFormTest from.");37
//}38

39
//获得对主窗体对象的引用,该对象实际上就是 Form1 应用程序(方法二)40
int numWaits = 0;41
do42
{43
Console.WriteLine("Looking for WinFormTest……");44
//查找第一个自动化元素45
aeForm = aeDeskTop.FindFirst(TreeScope.Children, new PropertyCondition(46
AutomationElement.NameProperty, "Form1"));47
++numWaits;48
Thread.Sleep(100);49
} while (null == aeForm && numWaits < 50);50
if (null == aeForm)51
throw new NullReferenceException("Failed to find WinFormTest.");52
else53
Console.WriteLine("Found it!");54

55
Console.WriteLine("Finding all user controls");56
//找到第一次出现的Button控件57
AutomationElement aeButton = aeForm.FindFirst(TreeScope.Children,58
new PropertyCondition(AutomationElement.NameProperty, "button1"));59

60
//找到所有的TextBox控件61
AutomationElementCollection aeAllTextBoxes = aeForm.FindAll(TreeScope.Children,62
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));63

64
// 控件初始化的顺序是先初始化后添加到控件65
// this.Controls.Add(this.textBox3); 66
// this.Controls.Add(this.textBox2);67
// this.Controls.Add(this.textBox1);68

69
AutomationElement aeTextBox1 = aeAllTextBoxes[2];70
AutomationElement aeTextBox2 = aeAllTextBoxes[1];71
AutomationElement aeTextBox3 = aeAllTextBoxes[0];72

73
Console.WriteLine("Settiing input to '30'");74
//通过ValuePattern设置TextBox1的值75
ValuePattern vpTextBox1 = (ValuePattern)aeTextBox1.GetCurrentPattern(ValuePattern.Pattern);76
vpTextBox1.SetValue("30");77
Console.WriteLine("Settiing input to '50'");78
//通过ValuePattern设置TextBox2的值79
ValuePattern vpTextBox2 = (ValuePattern)aeTextBox2.GetCurrentPattern(ValuePattern.Pattern);80
vpTextBox2.SetValue("50");81
Thread.Sleep(1500);82
Console.WriteLine("Clickinig on button1 Button.");83
//通过InvokePattern模拟点击按钮84
InvokePattern ipClickButton1 = (InvokePattern)aeButton.GetCurrentPattern(InvokePattern.Pattern);85
ipClickButton1.Invoke();86
Thread.Sleep(1500);87

88
//验证计算的结果与预期的结果是否相符合89
Console.WriteLine("Checking textBox3 for '80'");90
TextPattern tpTextBox3 = (TextPattern)aeTextBox3.GetCurrentPattern(TextPattern.Pattern);91
string result = tpTextBox3.DocumentRange.GetText(-1);//获取textbox3中的值92
//获取textbox3中的值93
//string result = (string)aeTextBox2.GetCurrentPropertyValue(ValuePattern.ValueProperty);94
if ("80" == result)95
{96
Console.WriteLine("Found it.");97
Console.WriteLine("TTest scenario: *PASS*");98
}99
else100
{101
Console.WriteLine("Did not find it.");102
Console.WriteLine("Test scenario: *FAIL*");103
}104

105
Console.WriteLine("Close application in 5 seconds.");106
Thread.Sleep(5000);107
//实现关闭被测试程序108
WindowPattern wpCloseForm = (WindowPattern)aeForm.GetCurrentPattern(WindowPattern.Pattern);109
wpCloseForm.Close();110

111
Console.WriteLine("\nEnd test run\n");112
}113
catch (Exception ex)114
{115
Console.WriteLine("Fatal error: " + ex.Message);116
}117
}118
}119
}120

使用UI Automation实现自动化测试--3
Chapter 3 UI Automation中的几个重要属性
Control Tree of the AutomationElement
在UI Automation控件树中,根节点为Desktop window, 其他运行在用户桌面的窗体都作为Desktop window的子节点。
如下图所示:
Desktop window可通过AutomationElement.RootElement属性获取,子节点中的窗体或对话框可通过
AutomationElement.RootElement.FindAll(TreeScope.Descendants, condition)
或
AutomationElement.RootElement.FindFirt(TreeScope.Descendants, condition)来获取.
AutomationElement property
在UI Automation中有如下几个重要属性:
- AutomationIdProperty: 通过AutomationId来查找AutomationElement。
- NameProperty:通过控件的Name属性来查找AutomationElement。
- ControlType:通过控件的类型来查找AutomationElement
- AutomationId: 唯一地标识自动化元素,将其与同级相区分。
- Name: WPF 按钮的Content 属性、Win32 按钮的Caption 属性以及 HTML 图像的ALT 属性都映射到 UI 自动化视图中的同一个属性 Name。
注:PropertyCondition类是用来对相关属性进行条件匹配,在控件树中查找控件时,可以通过最佳匹配来找到相应的控件。
如下代码列出了使用不同的属性来构建PropertyCondition,通过PropertyCondition来查找控件树中的控件.
public class PropertyConditions
{
static PropertyCondition propertyCondition;
/// <summary>
/// Create PropertyCondition by AutomationId
/// </summary>
/// <param name="automationId">Control AutomationId</param>
/// <returns>Return PropertyCondition instance</returns>
public static PropertyCondition GetAutomationIdProperty(object automationId)
{
propertyCondition = new PropertyCondition(AutomationElement.AutomationIdProperty, automationId);
return propertyCondition;
}
/// <summary>
///
/// </summary>
/// <param name="controlType"></param>
/// <returns></returns>
public static PropertyCondition GetControlTypeProperty(object controlType)
{
propertyCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, controlType);
return propertyCondition;
}
/// <summary>
///
/// </summary>
/// <param name="controlName"></param>
/// <returns></returns>
public static PropertyCondition GetNameProperty(object controlName)
{
propertyCondition = new PropertyCondition(AutomationElement.NameProperty, controlName);
return propertyCondition;
}
/// <summary>
/// Find element by specific PropertyCondition
/// </summary>
/// <param name="condition">PropertyCondition instance</param>
/// <returns>Target automation element</returns>
public static AutomationElement FindElement(PropertyCondition condition)
{
return AutomationElement.RootElement.FindFirst(TreeScope.Descendants, condition);
}
}
使用UI Automation实现自动化测试--4.1 (DockPattern)
DockPattern用于操作可停靠容器控件,我们最熟悉的VS2005/2008中的ToolBox,Solution Explorer都可以设置不同的DockPosition, 但是目前并不支持DockPattern,所以无法做为实例来讲。使用DockPattern的前提为控件支持DockPattern。 DockPattern中的DockPosition有六个枚举变量,即Bottom、Left、Right、Top、Fill和None。如果控件支持DockPattern, 则可以获取相对应的DockPosition以及设置控件的DockPosition。
如下代码是获取控件的DockPattern、获取控件当前的DockPosition以及设置控件的DockPosition。
#region DockPattern helper
/// <summary>
/// Get DockPattern
/// </summary>
/// <param name="element">AutomationElement instance</param>
/// <returns>DockPattern instance</returns>
public static DockPattern GetDockPattern(AutomationElement element)
{
object currentPattern;
if (!element.TryGetCurrentPattern(DockPattern.Pattern, out currentPattern))
{
throw new Exception(string.Format("Element with AutomationId '{0}' and Name '{1}' does not support the DockPattern.",
element.Current.AutomationId, element.Current.Name));
}
return currentPattern as DockPattern;
}
/// <summary>
/// Get DockPosition
/// </summary>
/// <param name="element">AutomationElement instance</param>
/// <returns>DockPosition instance</returns>
public static DockPosition GetDockPosition(AutomationElement element)
{
return GetDockPattern(element).Current.DockPosition;
}
/// <summary>
/// Set DockPosition
/// </summary>
/// <param name="element">AutomationElement instance</param>
public static void SetDockPattern(AutomationElement element, DockPosition dockPosition)
{
GetDockPattern(element).SetDockPosition(dockPosition);
}
#endregion
使用UI Automation实现自动化测试--4.2 (ExpandCollapsePattern)
ExpandCollapsePattern
表示以可视方式进行展开(以显示内容)和折叠(以隐藏内容)的控件。例如ComboBox控件支持ExpandCollapsePattern。
ExpandCollapsePattern有两个主要方法:
Expand()方法:隐藏 AutomationElement 的全部子代节点、控件或内容。
Collapse()方法:显示 AutomationElement 的全部子节点、控件或内容。
以下代码是用ExpandCollapsePattern来测试ComboBox控件的Expand和Collapse。
using System;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Windows.Automation;
namespace UIATest
{
class Program
{
static void Main(string[] args)
{
Process process = Process.Start(@"F:\CSharpDotNet\AutomationTest\ATP\WpfApp\bin\Debug\WpfApp.exe");
int processId = process.Id;
AutomationElement element = FindElementById(processId, "comboBox1");
ExpandCollapsePattern currentPattern = GetExpandCollapsePattern(element);
currentPattern.Expand();
Thread.Sleep(1000);
currentPattern.Collapse();
}
/// <summary>
/// Get the automation elemention of current form.
/// </summary>
/// <param name="processId">Process Id</param>
/// <returns>Target element</returns>
public static AutomationElement FindWindowByProcessId(int processId)
{
AutomationElement targetWindow = null;
int count = 0;
try
{
Process p = Process.GetProcessById(processId);
targetWindow = AutomationElement.FromHandle(p.MainWindowHandle);
return targetWindow;
}
catch (Exception ex)
{
count++;
StringBuilder sb = new StringBuilder();
string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString();
if (count > 5)
{
throw new InvalidProgramException(message, ex);
}
else
{
return FindWindowByProcessId(processId);
}
}
}

/// <summary>
/// Get the automation element by automation Id.
/// </summary>
/// <param name="windowName">Window name</param>
/// <param name="automationId">Control automation Id</param>
/// <returns>Automatin element searched by automation Id</returns>
public static AutomationElement FindElementById(int processId, string automationId)
{
AutomationElement aeForm = FindWindowByProcessId(processId);
AutomationElement tarFindElement = aeForm.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.AutomationIdProperty, automationId));
return tarFindElement;
}
ExpandCollapsePattern helper
}
}
以下代码为被测程序的xaml文件:
1
<Window x:Class="WpfApp.Window1"2
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"3
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"4
Title="Window1" Height="219" Width="353">5
<Grid>6
<ComboBox Name="comboBox1" Height="23" VerticalAlignment="Top" Margin="94,58,0,0" HorizontalAlignment="Left" Width="119">7
<ComboBoxItem>kaden</ComboBoxItem>8
<ComboBoxItem>sam</ComboBoxItem>9
</ComboBox>10
</Grid>11
</Window>
InvokePattern
InvokePattern是UIA中最常用的Pattern之一,WPF和Winform中的button控件都支持InvokePattern。
对InvokePattern的Invoke()方法的调用应立即返回,没有出现阻止情况。但是,此行为完全依赖于 Microsoft UI 自动化提供程序实现。在调用 Invoke() 会引起阻止问题(如Winform中的模式对话框,但是WPF中的对话框的处理方式和winform不同,所以可以使用Invoke()方法来操作WPF中的模式对话框,因为WPF中的模式对话框不会出现阻止的问题)的情况下,要调用此方法,则需要另起线程来操作。
using System;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Windows.Automation;
namespace UIATest
{
class Program
{
static void Main(string[] args)
{
Process process = Process.Start(@"F:\CSharpDotNet\AutomationTest\ATP\WpfApp\bin\Debug\WpfApp.exe");
int processId = process.Id;
AutomationElement element = FindElementById(processId, "button1");
InvokePattern currentPattern = GetInvokePattern(element);
currentPattern.Invoke();
}
/// <summary>
/// Get the automation elemention of current form.
/// </summary>
/// <param name="processId">Process Id</param>
/// <returns>Target element</returns>
public static AutomationElement FindWindowByProcessId(int processId)
{
AutomationElement targetWindow = null;
int count = 0;
try
{
Process p = Process.GetProcessById(processId);
targetWindow = AutomationElement.FromHandle(p.MainWindowHandle);
return targetWindow;
}
catch (Exception ex)
{
count++;
StringBuilder sb = new StringBuilder();
string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString();
if (count > 5)
{
throw new InvalidProgramException(message, ex);
}
else
{
return FindWindowByProcessId(processId);
}
}
}
/// <summary>
/// Get the automation element by automation Id.
/// </summary>
/// <param name="windowName">Window name</param>
/// <param name="automationId">Control automation Id</param>
/// <returns>Automatin element searched by automation Id</returns>
public static AutomationElement FindElementById(int processId, string automationId)
{
AutomationElement aeForm = FindWindowByProcessId(processId);
AutomationElement tarFindElement = aeForm.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.AutomationIdProperty, automationId));
return tarFindElement;
}
#region InvokePattern helper
/// <summary>
/// Get InvokePattern
/// </summary>
/// <param name="element">AutomationElement instance</param>
/// <returns>InvokePattern instance</returns>
public static InvokePattern GetInvokePattern(AutomationElement element)
{
object currentPattern;
if (!element.TryGetCurrentPattern(InvokePattern.Pattern, out currentPattern))
{
throw new Exception(string.Format("Element with AutomationId '{0}' and Name '{1}' does not support the InvokePattern.",
element.Current.AutomationId, element.Current.Name));
}
return currentPattern as InvokePattern;
}
#endregion
}
}
被测程序xaml代码如下:
<Window x:Class="WpfApp.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="219" Width="353">
<Grid>
<Button Height="23" HorizontalAlignment="Left" Click="button1_Click" Margin="50,0,0,62" Name="button1" VerticalAlignment="Bottom" Width="75">Button</Button>
</Grid>
</Window>
对应的cs文件:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Use InvokePattern invoke button.");
}
}
}
本文主要针对InvokePattern的Invoke方法来操作button控件。
使用UI Automation实现自动化测试--4.4 (ValuePattern)
ValuePattern是UI Automation中最常见的Pattern之一,Winform和WPF的TextBox控件都支持ValuePattern。
ValuePattern的一个重要的方法是SetValue,在允许调用 SetValue 之前,控件应将其 IsEnabledProperty 设置为 true 并将其 IsReadOnlyProperty 设置为 false。
通过ValuePattern的Current属性可以获得控件的value和IsReadOnly属性。
实现 Value 控件模式时,请注意以下准则和约定:
如果任何项的值是可编辑的,则诸如 ListItem 和 TreeItem 等控件必须支持 ValuePattern,而不管控件的当前编辑模式如何。如果子项是可编辑的,则父控件还必须支持ValuePattern。
下面的例子是通过ValuePattern来给TextBox设置和获取值:
1using System;
2using System.Text;
3using System.Diagnostics;
4using System.Threading;
5using System.Windows.Automation;
6
7namespace UIATest
8{
9 class Program
10 {
11 static void Main(string[] args)
12 {
13 Process process = Process.Start(@"F:\CSharpDotNet\AutomationTest\ATP\WpfApp\bin\Debug\WpfApp.exe");
14 int processId = process.Id;
15 AutomationElement element = FindElementById(processId, "textBox1");
16 ValuePattern currentPattern = GetValuePattern(element);
17 Console.WriteLine("Is read only:'{0}', TextBox text is:'{1}'", currentPattern.Current.IsReadOnly, currentPattern.Current.Value);
18 currentPattern.SetValue("KadenKang");
19 Console.WriteLine("After using the SetValue, the TextBox value is '{0}'", currentPattern.Current.Value);
20
21 }
22
23 /// <summary>
24 /// Get the automation elemention of current form.
25 /// </summary>
26 /// <param name="processId">Process Id</param>
27 /// <returns>Target element</returns>
28 public static AutomationElement FindWindowByProcessId(int processId)
29 {
30 AutomationElement targetWindow = null;
31 int count = 0;
32 try
33 {
34 Process p = Process.GetProcessById(processId);
35 targetWindow = AutomationElement.FromHandle(p.MainWindowHandle);
36 return targetWindow;
37 }
38 catch (Exception ex)
39 {
40 count++;
41 StringBuilder sb = new StringBuilder();
42 string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString();
43 if (count > 5)
44 {
45 throw new InvalidProgramException(message, ex);
46 }
47 else
48 {
49 return FindWindowByProcessId(processId);
50 }
51 }
52 }
53
54 /// <summary>
55 /// Get the automation element by automation Id.
56 /// </summary>
57 /// <param name="windowName">Window name</param>
58 /// <param name="automationId">Control automation Id</param>
59 /// <returns>Automatin element searched by automation Id</returns>
60 public static AutomationElement FindElementById(int processId, string automationId)
61 {
62 AutomationElement aeForm = FindWindowByProcessId(processId);
63 AutomationElement tarFindElement = aeForm.FindFirst(TreeScope.Descendants,
64 new PropertyCondition(AutomationElement.AutomationIdProperty, automationId));
65 return tarFindElement;
66 }
67
68 ValuePattern helper
87 }
88}
89
下面的代码是xaml设计:
1<Window x:Class="WpfApp.Window1"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 Title="Window1" Height="219" Width="353">
5 <Grid>
6 <TextBox Height="23" Margin="50,20,160,0" Name="textBox1" VerticalAlignment="Top" MaxLength="5">textBox text</TextBox>
7 </Grid>
8</Window>
9
本文通过简单的实例介绍了UI Automation中的ValuePattern及其使用方法。
使用UI Automation实现自动化测试--4.5 (WindowPattern)
WindowPattern 控件模式用于支持在传统的 图形用户界面 (GUI) 内提供基于基本窗口的功能的控件。必须实现此控件模式的控件的示例包括顶级应用程序窗口、多文档界面 (MDI) 子窗口、大小可调的拆分窗格控件、模式对话框以及气球状帮助窗口。可以使用WindowPattern来对window进行操作,例如验证window是否激活,是否最大化、最小化、正常模式以及关闭window等。
下面的代码演示了WindowPattern的使用方法:
1
using System;2
using System.Text;3
using System.Diagnostics;4
using System.Threading;5
using System.Windows.Automation;6

7
namespace UIATest8
{9
class Program10
{11
static void Main(string[] args)12
{13
Process process = Process.Start(@"F:\CSharpDotNet\AutomationTest\ATP\WpfApp\bin\Debug\WpfApp.exe");14
int processId = process.Id;15
AutomationElement element = FindWindowByProcessId(processId);16
WindowPattern currentPattern = GetWindowPattern(element);17
18
//Set window visual state to Maximized19
currentPattern.SetWindowVisualState(WindowVisualState.Maximized);20
Thread.Sleep(1000);21

22
//Set window visual state to Normal23
currentPattern.SetWindowVisualState(WindowVisualState.Normal);24
Thread.Sleep(1000);25

26
//Set window visual state to Minimized27
currentPattern.SetWindowVisualState(WindowVisualState.Minimized);28
29
//Close window30
currentPattern.Close(); 31
}32

33
/// <summary>34
/// Get the automation elemention of current form.35
/// </summary>36
/// <param name="processId">Process Id</param>37
/// <returns>Target element</returns>38
public static AutomationElement FindWindowByProcessId(int processId)39
{40
AutomationElement targetWindow = null;41
int count = 0;42
try43
{44
Process p = Process.GetProcessById(processId);45
targetWindow = AutomationElement.FromHandle(p.MainWindowHandle);46
return targetWindow;47
}48
catch (Exception ex)49
{50
count++;51
StringBuilder sb = new StringBuilder();52
string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString();53
if (count > 5)54
{55
throw new InvalidProgramException(message, ex);56
}57
else58
{59
return FindWindowByProcessId(processId);60
}61
}62
}63

64
WindowPattern helper83
}84
}
使用UI Automation实现自动化测试--4.6.1 (SelectionItemPattern)
SelectionItemPattern
支持SelectionItemPattern的控件有ListView、ListBox、RadioButton、GridView等。
- 1. SelectionItemPattern的三个重要方法:
- 1. AddToSelection:将当前元素添加到所选项的集合。
- 2. RemoveFromSelection: 从选定项的集合中移除当前元素。
- 3. Select: 取消所有已选中的项,然后选择当前元素。
- 2. SelectionItemPattern的Current属性
可通过Current属性的IsSelected属性来判断AutomationElement是否被selected.
如下代码演示了使用SelectionItemPattern来操作RadioButton控件。
1using System;
2using System.Text;
3using System.Diagnostics;
4using System.Threading;
5using System.Windows.Automation;
6
7namespace UIATest
8{
9 class Program
10 {
11 static void Main(string[] args)
12 {
13 Process process = Process.Start(@"F:\CSharpDotNet\AutomationTest\ATP\WpfApp\bin\Debug\WpfApp.exe");
14 int processId = process.Id;
15
16 AutomationElement element = FindElementById(processId, "radioButton1");
17 SelectionItemPattern selectionItemPattern = GetSelectionItemPattern(element);
18 selectionItemPattern.Select();
19 }
20
21 /// <summary>
22 /// Get the automation elemention of current form.
23 /// </summary>
24 /// <param name="processId">Process Id</param>
25 /// <returns>Target element</returns>
26 public static AutomationElement FindWindowByProcessId(int processId)
27 {
28 AutomationElement targetWindow = null;
29 int count = 0;
30 try
31 {
32 Process p = Process.GetProcessById(processId);
33 targetWindow = AutomationElement.FromHandle(p.MainWindowHandle);
34 return targetWindow;
35 }
36 catch (Exception ex)
37 {
38 count++;
39 StringBuilder sb = new StringBuilder();
40 string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString();
41 if (count > 5)
42 {
43 throw new InvalidProgramException(message, ex);
44 }
45 else
46 {
47 return FindWindowByProcessId(processId);
48 }
49 }
50 }
51
52
53 /// <summary>
54 /// Get the automation element by automation Id.
55 /// </summary>
56 /// <param name="windowName">Window name</param>
57 /// <param name="automationId">Control automation Id</param>
58 /// <returns>Automatin element searched by automation Id</returns>
59 public static AutomationElement FindElementById(int processId, string automationId)
60 {
61 AutomationElement aeForm = FindWindowByProcessId(processId);
62 AutomationElement tarFindElement = aeForm.FindFirst(TreeScope.Descendants,
63 new PropertyCondition(AutomationElement.AutomationIdProperty, automationId));
64 return tarFindElement;
65 }
66
67 SelectItemPattern
86 }
87}
88
以下代码为XAML:
1<Window x:Class="WpfApp.Window1"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 Title="Window1" Height="219" Width="353">
5 <Grid>
6 <RadioButton Height="16" HorizontalAlignment="Right" Margin="0,46,10,0" Name="radioButton1" VerticalAlignment="Top" Width="120">RadioButton</RadioButton>
7 </Grid>
8</Window>
9
本文简单介绍了SelectionItemPattern以及使用SelectionItemPattern来操作RadioButton。
使用UI Automation实现自动化测试--4.6.2 (SelectItemPattern Demo)
如下代码演示了使用SelectionItemPattern来实现listview item 的多选操作:
1
using System;2
using System.Text;3
using System.Diagnostics;4
using System.Threading;5
using System.Windows.Automation;6

7
namespace UIATest8
{9
class Program10
{11
static void Main(string[] args)12
{13
Process process = Process.Start(@"F:\CSharpDotNet\AutomationTest\ATP\WpfApp\bin\Debug\WpfApp.exe");14
int processId = process.Id;15

16
Thread.Sleep(1000);17
MutlSelect(new int[] { 0, 1 }, processId, false);18
}19

20
/// <summary>21
/// Get the automation elemention of current form.22
/// </summary>23
/// <param name="processId">Process Id</param>24
/// <returns>Target element</returns>25
public static AutomationElement FindWindowByProcessId(int processId)26
{27
AutomationElement targetWindow = null;28
int count = 0;29
try30
{31
Process p = Process.GetProcessById(processId);32
targetWindow = AutomationElement.FromHandle(p.MainWindowHandle);33
return targetWindow;34
}35
catch (Exception ex)36
{37
count++;38
StringBuilder sb = new StringBuilder();39
string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString();40
if (count > 5)41
{42
throw new InvalidProgramException(message, ex);43
}44
else45
{46
return FindWindowByProcessId(processId);47
}48
}49
}50

51

52
/// <summary>53
/// Get the automation element by automation Id.54
/// </summary>55
/// <param name="windowName">Window name</param>56
/// <param name="automationId">Control automation Id</param>57
/// <returns>Automatin element searched by automation Id</returns>58
public static AutomationElement FindElementById(int processId, string automationId)59
{60
AutomationElement aeForm = FindWindowByProcessId(processId);61
AutomationElement tarFindElement = aeForm.FindFirst(TreeScope.Descendants,62
new PropertyCondition(AutomationElement.AutomationIdProperty, automationId));63
return tarFindElement;64
}65
66
/// <summary>67
/// Bulk select the list item68
/// </summary>69
/// <param name="indexes">List item index collection</param>70
/// <param name="processId">Application process Id</param>71
/// <param name="isSelectAll">Is select all or not</param>72
public static void MutlSelect(int[] indexes, int processId, bool isSelectAll)73
{74
AutomationElement targetElement = FindElementById(processId, "listView1");75

76
AutomationElementCollection rows =77
targetElement.FindAll(TreeScope.Descendants,78
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem));79

80
object multiSelect;81

82
if (isSelectAll)83
{84
for (int i = 1; i < rows.Count - 1; i++)85
{86
if (rows[i].TryGetCurrentPattern(SelectionItemPattern.Pattern, out multiSelect))87
{88
(multiSelect as SelectionItemPattern).AddToSelection();89
}90
}91
}92
else93
{94
if (indexes.Length > 0)95
{96
for (int j = 0; j < indexes.Length; j++)97
{98
int tempIndex = indexes[j];99
if (rows[tempIndex].TryGetCurrentPattern(SelectionItemPattern.Pattern, out multiSelect))100
{101
(multiSelect as SelectionItemPattern).AddToSelection();102
}103
}104
}105
}106
}107

108
SelectItemPattern127
}128
}
如下代码为对应的XAML:
1
<Window x:Class="WpfApp.Window2"2
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"3
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"4
Title="Window2" Height="412" Width="585">5
<Grid>6
<ListView Margin="2,97,0,163" Name="listView1">7
<ListViewItem>Kaden</ListViewItem>8
<ListViewItem>KangYi</ListViewItem>9
<ListViewItem>John</ListViewItem>10
</ListView>11
</Grid>12
</Window>13
使用UI Automation实现自动化测试--4.7 (TogglePattern)
TogglePattern
支持TogglePattern的控件有CheckBox,TreeView中的button控件等。
- 1. TogglePattern的方法
Toggle方法用于操作可以循环通过的一组状态并在设置后保持某种状态。
- 2. TogglePattern属性
Current属性中的ToggleState有如下三种状态:
- 1. On
- 2. Off
- 3. Indeterminate
如下代码演示了使用TogglePattern来操作CheckBox控件。
1using System;
2using System.Text;
3using System.Diagnostics;
4using System.Threading;
5using System.Windows.Automation;
6
7namespace UIATest
8{
9 class Program
10 {
11 static void Main(string[] args)
12 {
13 Process process = Process.Start(@"F:\CSharpDotNet\AutomationTest\ATP\WpfApp\bin\Debug\WpfApp.exe");
14 int processId = process.Id;
15
16 Thread.Sleep(1000);
17 AutomationElement element = FindElementById(processId, "checkBox1");
18 TogglePattern togglePattern = GetTogglePattern(element);
19 togglePattern.Toggle();
20 }
21
22 /// <summary>
23 /// Get the automation elemention of current form.
24 /// </summary>
25 /// <param name="processId">Process Id</param>
26 /// <returns>Target element</returns>
27 public static AutomationElement FindWindowByProcessId(int processId)
28 {
29 AutomationElement targetWindow = null;
30 int count = 0;
31 try
32 {
33 Process p = Process.GetProcessById(processId);
34 targetWindow = AutomationElement.FromHandle(p.MainWindowHandle);
35 return targetWindow;
36 }
37 catch (Exception ex)
38 {
39 count++;
40 StringBuilder sb = new StringBuilder();
41 string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString();
42 if (count > 5)
43 {
44 throw new InvalidProgramException(message, ex);
45 }
46 else
47 {
48 return FindWindowByProcessId(processId);
49 }
50 }
51 }
52
53 /// <summary>
54 /// Get the automation element by automation Id.
55 /// </summary>
56 /// <param name="windowName">Window name</param>
57 /// <param name="automationId">Control automation Id</param>
58 /// <returns>Automatin element searched by automation Id</returns>
59 public static AutomationElement FindElementById(int processId, string automationId)
60 {
61 AutomationElement aeForm = FindWindowByProcessId(processId);
62 AutomationElement tarFindElement = aeForm.FindFirst(TreeScope.Descendants,
63 new PropertyCondition(AutomationElement.AutomationIdProperty, automationId));
64 return tarFindElement;
65 }
66
67 TogglePattern helper
83 }
84}
85
如下代码为对应的XAML:
1<Window x:Class="WpfApp.Window1"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 Title="Window1" Height="219" Width="353">
5 <Grid>
6 <CheckBox HorizontalAlignment="Right" Margin="0,75,10,89" Name="checkBox1" Width="120">CheckBox</CheckBox>
7 </Grid>
8</Window>
使用UI Automation实现自动化测试--4.8 (GridPattern)
GridPattern
支持GridPattern的最常见的控件为GridView, 在WPF中使用ListView和GridView组合即可得到相应的GridView。
GridPattern的方法
GetItem:此方法有两个参数,即DataGrid的Row和Column。
通过GridPattern的GetItem方法可以获取DataGrid中的某个确定的单元格,进而对单元进行操作。
对单元格的操作主要有以下几个方面:
- 1. 编辑单元个中的数据。
- 2. 获取单元格中的数据。
- 3. 获取单元格中嵌套的AutomationElement(一般使用与自定义控件中)。
GridPattern的属性
GridPattern的Current属性中有如下两个属性:
- 1. RowCount属性:GridPattern二维表格的行数。
- 2. ColumnCount属性:GridPattern二维表格列数。
下面我们通过一个实例来演示自动化测试中如何使用GridPattern来测试GridView的方法:
1using System;
2using System.Text;
3using System.Diagnostics;
4using System.Threading;
5using System.Windows.Automation;
6
7namespace UIATest
8{
9 class Program
10 {
11 static void Main(string[] args)
12 {
13 Process process = Process.Start(@"F:\CSharpDotNet\AutomationTest\ATP\WpfApp\bin\Debug\WpfApp.exe");
14 int processId = process.Id;
15 Thread.Sleep(1000);
16
17 GridPattern Test
30 }
31
32 /// <summary>
33 /// Get the automation elemention of current form.
34 /// </summary>
35 /// <param name="processId">Process Id</param>
36 /// <returns>Target element</returns>
37 public static AutomationElement FindWindowByProcessId(int processId)
38 {
39 AutomationElement targetWindow = null;
40 int count = 0;
41 try
42 {
43 Process p = Process.GetProcessById(processId);
44 targetWindow = AutomationElement.FromHandle(p.MainWindowHandle);
45 return targetWindow;
46 }
47 catch (Exception ex)
48 {
49 count++;
50 StringBuilder sb = new StringBuilder();
51 string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString();
52 if (count > 5)
53 {
54 throw new InvalidProgramException(message, ex);
55 }
56 else
57 {
58 return FindWindowByProcessId(processId);
59 }
60 }
61 }
62
63 /// <summary>
64 /// Get the automation element by automation Id.
65 /// </summary>
66 /// <param name="windowName">Window name</param>
67 /// <param name="automationId">Control automation Id</param>
68 /// <returns>Automatin element searched by automation Id</returns>
69 public static AutomationElement FindElementById(int processId, string automationId)
70 {
71 AutomationElement aeForm = FindWindowByProcessId(processId);
72 AutomationElement tarFindElement = aeForm.FindFirst(TreeScope.Descendants,
73 new PropertyCondition(AutomationElement.AutomationIdProperty, automationId));
74 return tarFindElement;
75 }
76
77 GridPattern helper
95 }
96}
97
对应的XAML代码如下:
1<Window x:Class="WpfApp.GridView"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 Title="GridView" Height="426" Width="558">
5 <Grid>
6 <!--将鼠标放在方框的边缘点击就会产生相应的分割线生成Grid.RowDefinitions-->
7 <Grid.RowDefinitions>
8 <!--Auto,实际作用就是取实际控件所需的最小值;值为*或N*,实际作用就是取尽可能大的值;数字,绝对尺寸-->
9 <RowDefinition Height="*" />
10 <RowDefinition Height="auto" MinHeight="95" />
11 <RowDefinition Height="22" />
12 </Grid.RowDefinitions>
13 <ListView Name="listview1" MinWidth="280" Grid.RowSpan="2" MouseMove="listview1_MouseMove">
14 <ListView.View>
15 <GridView x:Name="gridView1">
16 <GridViewColumn Header="EmployeeID" DisplayMemberBinding="{Binding Path=EmployeeID}"></GridViewColumn>
17 <GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding Path=FirstName}"></GridViewColumn>
18 <GridViewColumn Header="LastName" DisplayMemberBinding="{Binding Path=LastName}"></GridViewColumn>
19 <GridViewColumn Header="Address" DisplayMemberBinding="{Binding Path=Address}"></GridViewColumn>
20 </GridView>
21 </ListView.View>
22 </ListView>
23 </Grid>
24
25</Window>
26
GridView窗体后台代码如下:
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.Windows;
6using System.Windows.Controls;
7using System.Windows.Data;
8using System.Windows.Documents;
9using System.Windows.Input;
10using System.Windows.Media;
11using System.Windows.Media.Imaging;
12using System.Windows.Shapes;
13using System.Data.SqlClient;
14using System.Data;
15
16namespace WpfApp
17{
18 /// <summary>
19 /// Interaction logic for GridView.xaml
20 /// </summary>
21 public partial class GridView : Window
22 {
23 public GridView()
24 {
25 InitializeComponent();
26 getData();
27 }
28 SqlDataAdapter sda;
29 DataTable dt;
30 void getData()
31 {
32 //Northwind database download path:http://download.csdn.net/down/845087/beyondchina123
33 //init sqlconnection
34 SqlConnectionStringBuilder connbuilder = new SqlConnectionStringBuilder();
35 connbuilder.DataSource = ".";//本地服务器
36 connbuilder.IntegratedSecurity = true;//Windows集成验证
37 connbuilder.InitialCatalog = "Northwind";//数据库为Northwind
38 SqlConnection conn = new SqlConnection(connbuilder.ConnectionString);
39 sda = new SqlDataAdapter("select EmployeeID,FirstName,LastName,Address from Employees ", conn);
40 SqlCommandBuilder commbuilder = new SqlCommandBuilder(sda);
41 dt = new DataTable();
42 sda.Fill(dt);
43 listview1.ItemsSource = dt.DefaultView;
44 }
45 }
46}
47
48
本文主要简单介绍了GridPattern以及GridPattern在测试中是使用方法。
使用UI Automation实现自动化测试--4.9 (ScrollPattern)
ScrollPattern
ScrollPattern是用来操作控件的滚动条,目前支持ScrollPattern的控件有ListBox,listView,GridView,TreeView.
ScrollPattern主要方法
- 1. Scroll 水平和垂直滚动内容区域的可见区域滚动, Scroll有两个参数,其类型为ScrollAmount枚举类型。
- 2. ScrollHorizontal 按指定的 ScrollAmount 水平滚动内容区域的当前可见区域滚动。
- 3. ScrollVertical 按指定的 ScrollAmount 垂直滚动内容区域的当前可见区域滚动。
ScrollPattern属性
- VerticallyScrollable 属性用于判定是否可以垂直滚动。
- HorizontallyScrollable 属性用于判定是否可以水平滚动。
- HorizontalScrollPercent 获取当前水平滚动条的位置。
- VerticalScrollPercent 获取当前垂直滚动条的位置。
下面我们通过一个实例来演示自动化测试中如何使用ScrollPattern来测试GridView中滚动条的方法:
1using System;
2using System.Text;
3using System.Diagnostics;
4using System.Threading;
5using System.Windows.Automation;
6
7namespace UIATest
8{
9 class Program
10 {
11 static void Main(string[] args)
12 {
13 Process process = Process.Start(@"F:\CSharpDotNet\AutomationTest\ATP\WpfApp\bin\Debug\WpfApp.exe");
14 int processId = process.Id;
15 Thread.Sleep(1000);
16
17 ScrollPattern
31
32 }
33
34 /// <summary>
35 /// Get the automation elemention of current form.
36 /// </summary>
37 /// <param name="processId">Process Id</param>
38 /// <returns>Target element</returns>
39 public static AutomationElement FindWindowByProcessId(int processId)
40 {
41 AutomationElement targetWindow = null;
42 int count = 0;
43 try
44 {
45 Process p = Process.GetProcessById(processId);
46 targetWindow = AutomationElement.FromHandle(p.MainWindowHandle);
47 return targetWindow;
48 }
49 catch (Exception ex)
50 {
51 count++;
52 StringBuilder sb = new StringBuilder();
53 string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString();
54 if (count > 5)
55 {
56 throw new InvalidProgramException(message, ex);
57 }
58 else
59 {
60 return FindWindowByProcessId(processId);
61 }
62 }
63 }
64
65
66 /// <summary>
67 /// Get the automation element by automation Id.
68 /// </summary>
69 /// <param name="windowName">Window name</param>
70 /// <param name="automationId">Control automation Id</param>
71 /// <returns>Automatin element searched by automation Id</returns>
72 public static AutomationElement FindElementById(int processId, string automationId)
73 {
74 AutomationElement aeForm = FindWindowByProcessId(processId);
75 AutomationElement tarFindElement = aeForm.FindFirst(TreeScope.Descendants,
76 new PropertyCondition(AutomationElement.AutomationIdProperty, automationId));
77 return tarFindElement;
78 }
79
80 GetScrollPattern helper
98 }
99}
100
XAML源码:
1<Window x:Class="WpfApp.GridView"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 Title="GridView" Height="280" Width="467">
5 <Grid>
6 <!--将鼠标放在方框的边缘点击就会产生相应的分割线生成Grid.RowDefinitions-->
7 <Grid.RowDefinitions>
8 <!--Auto,实际作用就是取实际控件所需的最小值;值为*或N*,实际作用就是取尽可能大的值;数字,绝对尺寸-->
9 <RowDefinition Height="*" />
10 <RowDefinition Height="auto" MinHeight="95" />
11 <RowDefinition Height="22" />
12 </Grid.RowDefinitions>
13 <ListView Name="listview1" MinWidth="280" Grid.RowSpan="2" MouseMove="listview1_MouseMove">
14 <ListView.View>
15 <GridView x:Name="gridView1">
16 <GridViewColumn Header="EmployeeID" DisplayMemberBinding="{Binding Path=EmployeeID}"></GridViewColumn>
17 <GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding Path=FirstName}"></GridViewColumn>
18 <GridViewColumn Header="LastName" DisplayMemberBinding="{Binding Path=LastName}"></GridViewColumn>
19 <GridViewColumn Header="Address" DisplayMemberBinding="{Binding Path=Address}"></GridViewColumn>
20 </GridView>
21 </ListView.View>
22 </ListView>
23 <!--Grid.Row="1"用来设置WrapPanel及Button应该在父容器的什么位置-->
24 </Grid>
25
26</Window>
后台CS源码:
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.Windows;
6using System.Windows.Controls;
7using System.Windows.Data;
8using System.Windows.Documents;
9using System.Windows.Input;
10using System.Windows.Media;
11using System.Windows.Media.Imaging;
12using System.Windows.Shapes;
13using System.Data.SqlClient;
14using System.Data;
15
16namespace WpfApp
17{
18 /// <summary>
19 /// Interaction logic for GridView.xaml
20 /// </summary>
21 public partial class GridView : Window
22 {
23 public GridView()
24 {
25 InitializeComponent();
26 getData();
27 }
28 SqlDataAdapter sda;
29 DataTable dt;
30 void getData()
31 {
32 //init sqlconnection
33 SqlConnectionStringBuilder connbuilder = new SqlConnectionStringBuilder();
34 connbuilder.DataSource = ".";//本地服务器
35 connbuilder.IntegratedSecurity = true;//Windows集成验证
36 connbuilder.InitialCatalog = "TestDB";//数据库为Northwind
37 SqlConnection conn = new SqlConnection(connbuilder.ConnectionString);
38 sda = new SqlDataAdapter("select EmployeeID,FirstName,LastName,Address from Employees ", conn);
39 SqlCommandBuilder commbuilder = new SqlCommandBuilder(sda);
40 //sda.UpdateCommand = commbuilder.GetUpdateCommand();
41 dt = new DataTable();
42 //sda.AcceptChangesDuringUpdate = true;
43 sda.Fill(dt);
44 listview1.ItemsSource = dt.DefaultView;
45 }
46 }
47}
48
49
数据库及数据库表源码:
1USE [master]
2GO
3/****** Object: Database TestDB Script Date: 10/17/2009 16:08:09 ******/
4IF EXISTS (SELECT name FROM sys.databases WHERE name = N'TestDB')
5DROP DATABASE [TestDB]
6CREATE DATABASE TestDB ON PRIMARY
7( NAME = N'TestDB', FILENAME = N'C:\TestDB.mdf' , SIZE = 2688KB , MAXSIZE = UNLIMITED, FILEGROWTH = 80KB )
8 LOG ON
9( NAME = N'TestDB_log', FILENAME = N'C:\TestDB.ldf' , SIZE = 1024KB , MAXSIZE = UNLIMITED, FILEGROWTH = 10%)
10GO
11
12use TestDB
13
14CREATE TABLE [dbo].[Employees](
15 [EmployeeID] [int] IDENTITY(1,1) primary key NOT NULL,
16 [LastName] [varchar](20) NOT NULL,
17 [FirstName] [varchar](10) NOT NULL,
18 [Address] [varchar](60) NULL
19)
20
21GO
22truncate table employees
23declare @i int
24set @i = 1
25while @i<25
26begin
27
28insert into Employees
29(
30 LastName,
31 FirstName,
32 [Address]
33)
34values
35('Kaden'+cast(@i as varchar), 'Kang'+cast(@i as varchar), 'Nanjing, Jiangsu, China'+cast(@i as varchar))
36set @i=@i+1
37end
38
39
本文简单介绍了ScrollPattern以及使用ScrollPattern来操作垂直水平滚动条。


浙公网安备 33010602011771号