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
2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

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。








































































以下代码为被测程序的xaml文件:

1

2

3

4

5

6

7

8

9

10

11

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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

83

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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

127

128

如下代码为对应的XAML:

1

2

3

4

5

6

7

8

9

10

11

12

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来操作垂直水平滚动条。