使用UI Automation实现自动化测试--5-7

使用UI Automation实现自动化测试--5 (Winfrom和WPF中弹出和关闭对话框的不同处理方式)

在使用UI Automation对Winform和WPF的程序测试中发现有一些不同的地方,而这些不同来自于Winform与WPF的处理机制不同。下面我们通过一个简单的实例来加以说明:

实例描述

我们使用InvokePattern来点击按钮弹出一个对话框,然后点击对话框中的“确定”按钮关闭对话框。

两种方式对比

首先我们使用如下代码来针对Winfom和WPF分别进行测试:

1public static void ClickButtonById(int processId, string buttonId)
 2{
 3    AutomationElement element = FindElementById(processId, buttonId);
 4    if (element == null)
 5    {
 6        throw new NullReferenceException(string.Format("Element with AutomationId ‘{0}‘ can not be find.", element.Current.Name));
 7    }
 8    InvokePattern currentPattern = GetInvokePattern(element);
 9    currentPattern.Invoke();
10}

上面的代码主要是用来点击按钮,我们的目的是点击按钮弹出MessageBox,然后点击MessageBox中的“OK”按钮关闭此对话框。

通过测试结果发现,上面的代码在WPF程序中完全可以通过,但是在Winform程序中,点击按钮弹出对话框之后发生阻塞现象,导致程序无法向下执行,所以我们通过如上代码视图点击MessageBox中的按钮来关闭此MessageBox将不可能实现,原因就在于Winform中的MessageBox弹出后就会出现阻塞现象,而WPF中使用了另一种处理方式(对此笔者解释的不够深刻,欢迎广大高手帮忙指正, 另外,此问题在Windows 7操作系统上面不会呈现,也可能与操作系统中API对UI Automation的支持有关)。

解决方案

那么我们通过什么方式来解决此问题呢?很多人会想到多线程,但是我们也可以通过发送异步消息来达到相应的效果。下面我们就通过多线程和发送异步消息的方式来方式来点击Winform中MessageBox中的“OK”按钮,此方法同样可以点击WPF中MessageBox中的按钮达到关闭对话框的效果。

  1. 1.       多线程的方式

1public static void ClickButtonById(int processId, string buttonId)
 2{
 3    AutomationElement element = FindElementById(processId, buttonId);
 4
 5    if (element == null)
 6    {
 7        throw new NullReferenceException(string.Format("Element with AutomationId ‘{0}‘ can not be find.", element.Current.AutomationId));
 8    }
 9
10    ThreadStart start2 = null;
11
12    if (start2 == null)
13    {
14        start2 = delegate
15        {
16            object obj2;
17            if (!element.TryGetCurrentPattern(InvokePattern.Pattern, out obj2))
18            {
19                throw new Exception(string.Format("Button element with AutomationId ‘{0}‘ does not support the InvokePattern.", element.Current.AutomationId));
20            }
21            (obj2 as InvokePattern).Invoke();
22        };
23    }
24    ThreadStart start = start2;
25    new Thread(start).Start();
26    Thread.Sleep(0xbb8);
27}

  1. 1.       发送异步消息的方式

1[DllImport("user32.dll", CharSet = CharSet.Auto)]
 2private static extern IntPtr PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
 3[DllImport("user32.dll", CharSet = CharSet.Auto)]
 4private static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
 5
 6public static void ClickButtonById(this Tester tester, int processId, string buttonId)
 7{
 8    AutomationElement element = FindElementById(processId, buttonId);
 9    if (element == null)
10    {
11        throw new NullReferenceException(string.Format("Element with AutomationId ‘{0}‘ can not be find.", element.Current.AutomationId));
12    }
13
14    IntPtr nativeWindowHandle = (IntPtr)element.Current.NativeWindowHandle;
15    if (nativeWindowHandle != IntPtr.Zero)
16    {
17        HandleRef hWnd = new HandleRef(nativeWindowHandle, nativeWindowHandle);
18        PostMessage(hWnd, 0xf5, IntPtr.Zero, IntPtr.Zero);
19    }
20    }
21}

相关FindWindow和FindElement代码:

1/// <summary>
 2/// Get the automation elemention of current form.
 3/// </summary>
 4/// <param name="processId">Process Id</param>
 5/// <returns>Target element</returns>
 6public static AutomationElement FindWindowByProcessId(int processId)
 7{
 8    AutomationElement targetWindow = null;
 9    int count = 0;
10    try
11    {
12        Process p = Process.GetProcessById(processId);
13        targetWindow = AutomationElement.FromHandle(p.MainWindowHandle);
14        return targetWindow;
15    }
16    catch (Exception ex)
17    {
18        count++;
19        StringBuilder sb = new StringBuilder();
20        string message = sb.AppendLine(string.Format("Target window is not existing.try #{0}", count)).ToString();
21        if (count > 5)
22        {
23            throw new InvalidProgramException(message, ex);
24        }
25        else
26        {
27            return FindWindowByProcessId(processId);
28        }
29    }
30}
31
32
33/// <summary>
34/// Get the automation element by automation Id.
35/// </summary>
36/// <param name="windowName">Window name</param>
37/// <param name="automationId">Control automation Id</param>
38/// <returns>Automatin element searched by automation Id</returns>
39public static AutomationElement FindElementById(int processId, string automationId)
40{
41    AutomationElement aeForm = FindWindowByProcessId(processId);
42    AutomationElement tarFindElement = aeForm.FindFirst(TreeScope.Descendants,
43    new PropertyCondition(AutomationElement.AutomationIdProperty, automationId));
44    return tarFindElement;
45}

本节主要针对在非Window 7操作系统上面出现的有关WPF和Winform中的MessageBox关闭问题作了简单的探讨。

使用UI Automation实现自动化测试--6 (模拟鼠标在自动化测试中的应用)

模拟鼠标在自动化测试中的应用

尽管UI Automation可以实现控件的自动化,但是,自定义控件的自动化往往由于自定义控件中对自动化的支持不够,而导致无法使用UI Automation的相关Pattern来操作此控件。此时我们可以使用模拟鼠标的操作来达到相应的效果。

. NET并没有提供改变鼠标指针位置、模拟点击操作的函数;但是Windows API提供了。在.NET中模拟鼠标通常是调用底层的API来实现。


注:在使用前必须添加using System.Runtime.InteropServices;命名空间。

移动鼠标到指定的位置


/// <summary>

/// Add mouse move event

/// </summary>

/// <param name="x">Move to specify x coordinate</param>

/// <param name="y">Move to specify y coordinate</param>

/// <returns></returns>

 [DllImport("user32.dll")]

extern static bool SetCursorPos(int x, int y);

该函数可以改变鼠标指针的位置。其中x,y是相对于屏幕左上角的绝对位置。

移动鼠标到指定的位置并进行相应的鼠标操作


/// <summary>

/// Mouse click event

/// </summary>

/// <param name="mouseEventFlag">MouseEventFlag </param>

/// <param name="incrementX">X coordinate</param>

/// <param name="incrementY">Y coordinate</param>

/// <param name="data"></param>

/// <param name="extraInfo"></param>

[DllImport("user32.dll")]

extern static void mouse_event(int mouseEventFlag, int incrementX, intincrementY, uint data, UIntPtr extraInfo);

这个函数不仅可以设置鼠标指针绝对的位置,而且可以以相对坐标来设置。另外,该函数还可以模拟鼠标左右键点击、鼠标滚轮操作等。其中的MouseEventFlag是一个常量,定义如下:


const int MOUSEEVENTF_MOVE = 0x0001;

const int MOUSEEVENTF_LEFTDOWN = 0x0002;

const int MOUSEEVENTF_LEFTUP = 0x0004;

const int MOUSEEVENTF_RIGHTDOWN = 0x0008;

const int MOUSEEVENTF_RIGHTUP = 0x0010;

const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;

const int MOUSEEVENTF_MIDDLEUP = 0x0040;

const int MOUSEEVENTF_ABSOLUTE = 0x8000;

上面列举的两个函数的详细说明,可参考MSDN帮助文档。

如下代码为在控件的指定位置上单击鼠标左键和右键。


public void ClickLeftMouse(int processId, string automationId)

{

AutomationElement element = FindElementById(processId, automationId);

if (element == null)

{

throw new NullReferenceException(string.Format("Element with AutomationId ‘{0}‘ and Name ‘{1}‘ can not be find.",

element.Current.AutomationId, element.Current.Name));

}

Rect rect = element.Current.BoundingRectangle;

int IncrementX = (int)(rect.Left + rect.Width / 2);

int IncrementY = (int)(rect.Top + rect.Height / 2);

//Make the cursor position to the element.

SetCursorPos(IncrementX, IncrementY);

//Make the left mouse down and up.

mouse_event(MOUSEEVENTF_LEFTDOWN, IncrementX, IncrementY, 0, 0);

mouse_event(MOUSEEVENTF_LEFTUP, IncrementX, IncrementY, 0, 0);

}

public void ClickRightMouse(int processId, string automationId)

{

AutomationElement element = FindElementById(processId, automationId);

if (element == null)

{

throw new NullReferenceException(string.Format("Element with AutomationId ‘{0}‘ and Name ‘{1}‘ can not be find.",

element.Current.AutomationId, element.Current.Name));

}

Rect rect = element.Current.BoundingRectangle;

int IncrementX = (int)(rect.Left + rect.Width / 2);

int IncrementY = (int)(rect.Top + rect.Height / 2);

//Make the cursor position to the element.

SetCursorPos(IncrementX, IncrementY);

//Make the left mouse down and up.

mouse_event(MOUSEEVENTF_RIGHTDOWN, IncrementX, IncrementY, 0, 0);

mouse_event(MOUSEEVENTF_RIGHTUP, IncrementX, IncrementY, 0, 0);

}

在上面的代码中,我们通过如下步骤来达到点击鼠标的目的:

  1. 1.       通过UI Automation来找到需要鼠标点击的控件;
  2. 2.       然后取得此控件的坐标;
  3. 3.       在设置点击坐标在控件的中心位置;
  4. 4.       移动鼠标的位置到控件的中心位置(此步骤可以不要,但是为了达到双保险的效果,我们还是移动一次)。
  5. 5.       调用Win32 API模拟鼠标点击控件。

注:Rect需要引用WindowBase.dll,添加using System.Windows;明明空间。

    下面的例子演示了通过模拟鼠标左键点击button来弹出对话框,并且模拟鼠标点击对话框中的“OK”按钮关闭对话框。


using System;

using System.Text;

using System.Diagnostics;

using System.Threading;

using System.Windows.Automation;

using System.Runtime.InteropServices;

using System.Windows;

namespace UIATest

{

class Program

{

static void Main(string[] args)

{

Process process =Process.Start(@"F:\CSharpDotNet\AutomationTest\ATP\ATP.TestForm\bin\Debug\ATP.TestForm.exe");

int processId = process.Id;

Thread.Sleep(1000);

ClickLeftMouse(processId, "button1");

Thread.Sleep(3000);

ClickLeftMouse(processId, "2");

Console.WriteLine("Test finised.");

}

#region ClickMouse

#region Import DLL

/// <summary>

/// Add mouse move event

/// </summary>

/// <param name="x">Move to specify x coordinate</param>

/// <param name="y">Move to specify y coordinate</param>

/// <returns></returns>

[DllImport("user32.dll")]

extern static bool SetCursorPos(int x, int y);

/// <summary>

/// Mouse click event

/// </summary>

/// <param name="mouseEventFlag">MouseEventFlag </param>

/// <param name="incrementX">X coordinate</param>

/// <param name="incrementY">Y coordinate</param>

/// <param name="data"></param>

/// <param name="extraInfo"></param>

[DllImport("user32.dll")]

extern static void mouse_event(int mouseEventFlag, int incrementX, int incrementY, uint data, UIntPtrextraInfo);

const int MOUSEEVENTF_MOVE = 0x0001;

const int MOUSEEVENTF_LEFTDOWN = 0x0002;

const int MOUSEEVENTF_LEFTUP = 0x0004;

const int MOUSEEVENTF_RIGHTDOWN = 0x0008;

const int MOUSEEVENTF_RIGHTUP = 0x0010;

const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;

const int MOUSEEVENTF_MIDDLEUP = 0x0040;

const int MOUSEEVENTF_ABSOLUTE = 0x8000;

#endregion

public static void ClickLeftMouse(int processId, string automationId)

{

AutomationElement element = FindElementById(processId, automationId);

if (element == null)

{

throw new NullReferenceException(string.Format("Element with AutomationId ‘{0}‘ and Name ‘{1}‘ can not be find.",

element.Current.AutomationId, element.Current.Name));

}

Rect rect = element.Current.BoundingRectangle;

int IncrementX = (int)(rect.Left + rect.Width / 2);

int IncrementY = (int)(rect.Top + rect.Height / 2);

//Make the cursor position to the element.

SetCursorPos(IncrementX, IncrementY);

//Make the left mouse down and up.

mouse_event(MOUSEEVENTF_LEFTDOWN, IncrementX, IncrementY, 0, UIntPtr.Zero);

mouse_event(MOUSEEVENTF_LEFTUP, IncrementX, IncrementY, 0, UIntPtr.Zero);

}

#endregion

/// <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;

}

}

}

Summary

本节首先简单介绍了如何在.NET中模拟鼠标操作,进而通过实例演示了模拟鼠标在基于UI Automation的自动化测试中应用。

使用UI Automation实现自动化测试--7.1 (模拟键盘输入数据在自动化测试中的应用)

Chapter 7 Simulated keyboard

7.1 模拟键盘输入

7.1.1 Introduction

此部分我们通过System.Windows.Forms.SendKeys类中的Send方法来模拟通过键盘输入数据到相应的控件中。

7.1.2 SendKeys Class Introduction(MSDN)

使用 SendKeys 将键击和组合键击发送到活动应用程序。此类无法实例化。若要发送一个键击给某个类并立即继续程序流,请使用 Send。若要等待键击启动的任何进程,请使用 SendWait

每个键都由一个或多个字符表示。若要指定单个键盘字符,请使用该字符本身。例如,若要表示字母 A,请将字符串“A”传递给方法。若要表示多个字符,请将各个附加字符追加到它之前的字符的后面。若要表示字母 A、B 和 C,请将参数指定为“ABC”。

加号 (+)、插入符号 (^)、百分号 (%)、波浪号 (~) 以及圆括号 ( ) 对 SendKeys 具有特殊含义。若要指定这些字符中的某个字符,请将其放在大括号 ({}) 内。例如,若要指定加号,请使用“{+}”。若要指定大括号字符,请使用“{{}”和“{}}”。中括号 ([ ]) 对 SendKeys 没有特殊含义,但必须将它们放在大括号内。在其他应用程序中,中括号具有特殊含义,此含义可能会在发生动态数据交换 (DDE) 时起重要作用。

7.1.3 SendKeys Method(MSDN)

SendKeys有两个重要的方法:

  1. 1.         Send方法:向活动应用程序发送击键。
  2. 2.         SendWait方法:向活动应用程序发送给定的键,然后等待消息被处理。

有关SendKeys的参考:

英文站点:http://msdn.microsoft.com/en-us/library/8c6yea83(VS.85).aspx

中文站点:http://msdn.microsoft.com/zh-cn/library/system.windows.forms.sendkeys_methods(VS.80).aspx

下面我们通过一个实例来演示通过SendKeys类来模拟键盘操作进行数据输入和快捷菜单的打开:


using System;

using System.Text;

using System.Diagnostics;

using System.Threading;

using System.Windows.Automation;

using System.Runtime.InteropServices;

using System.Windows;

using System.Windows.Forms;

namespace UIATest

{

class Program

{

static void Main(string[] args)

{

Process process = Process.Start(@"E:\WorkBook\ATP\WpfApp\bin\Debug\WpfApp.exe");

int processId = process.Id;

AutomationElement element = FindElementById(processId, "textBox1");

SendKeys sendkeys = new SendKeys();

sendkeys.Sendkeys(element, "Sending keys to input data");

Console.WriteLine(sendkeys.ToString());

sendkeys.Sendkeys(element, sendkeys.ContextMenu);

Console.WriteLine(sendkeys.ToString());

Console.WriteLine("Test finised.");

}

/// <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;

}

}

}

我们自定义一个SendKeys类来对已有的SendKeys类进行优化,代码如下:


using System;

using System.Windows.Automation;

using System.Threading;

using System.Text;

using System.Windows.Forms;

namespace UIATest

{

public class SendKeys

{

StringBuilder builder = new StringBuilder();

public string Alt = "%";

public string ContextMenu = "+{F10}";

public string Ctrl = "^";

public string Shift = "+";

public string Enter = "{Enter}";

public string Delete = "{Del}";

public string Save = "^S";

public string SaveAll = "^+S";

public string Copy = "^C";

public string Cut = "^X";

public string Paste = "^V";

public string Undo = "^Z";

public string Redo = "^Y";

public string Print = "^P";

public string Help = "{F1}";

public string New = "^N";

public string[] Keys{ get; set; }

public void Sendkeys(AutomationElement element, string[] keys)

{

this.Keys = keys;

try

{

element.SetFocus();

}

catch (Exception exception)

{

throw new Exception("Cannot set focus to this element.", exception);

}

string myKeys = "";

foreach (string str2 in this.Keys)

{

myKeys = myKeys + str2;

}

Thread.Sleep(200);

if ((this.ContainsUnescapedKey(myKeys, ‘^‘) || this.ContainsUnescapedKey(myKeys, ‘%‘)) ||this.ContainsUnescapedKey(myKeys, ‘+‘))

{

myKeys = myKeys.ToLower();

}

System.Windows.Forms.SendKeys.SendWait(myKeys);

Thread.Sleep(0x3e8);

}

public void Sendkeys(AutomationElement element, string myKeys)

{

this.Keys = new string[1];

this.Keys[0] = myKeys;

try

{

element.SetFocus();

}

catch (Exception exception)

{

throw new Exception("Cannot set focus to this element.", exception);

}

Thread.Sleep(200);

if ((this.ContainsUnescapedKey(myKeys, ‘^‘) || this.ContainsUnescapedKey(myKeys, ‘%‘)) ||this.ContainsUnescapedKey(myKeys, ‘+‘))

{

myKeys = myKeys.ToLower();

}

System.Windows.Forms.SendKeys.SendWait(myKeys);

Thread.Sleep(0x3e8);

}

private bool ContainsUnescapedKey(string keys, char key)

{

for (int i = 0; i < keys.Length; i++)

{

if (keys[i] == key)

{

if ((i == 0) || (i == (keys.Length - 1)))

{

return true;

}

if ((keys[i - 1] != ‘{‘) || (keys[i + 1] != ‘}‘))

{

return true;

}

}

}

return false;

}

private string KeysToString(string[] keys)

{

if (keys != null)

{

for (int i = 0; i < keys.Length; i++)

{

string str = keys[i];

if (str == null)

{

builder.Append(keys[i]);

}

int length = keys.Length - 1;

switch (str)

{

case "^":

builder.Append("Ctrl");

IsEquals(i, length, builder);

break;

case "+{F10}":

builder.Append("Open Context Menu");

IsEquals(i, length, builder);

break;

case "%":

builder.Append("Alt");

IsEquals(i, length, builder);

break;

case "+":

builder.Append("Shift");

IsEquals(i, length, builder);

break;

case "^S":

builder.Append("Save");

IsEquals(i, length, builder);

break;

case "^X":

builder.Append("Cut");

IsEquals(i, length, builder);

break;

case "^C":

builder.Append("Copy");

IsEquals(i, length, builder);

break;

case "^V":

builder.Append("Paste");

IsEquals(i, length, builder);

break;

case "^+S":

builder.Append("Save All");

IsEquals(i, length, builder);

break;

case "^P":

builder.Append("Print");

IsEquals(i, length, builder);

break;

case "^Z":

builder.Append("Undo");

IsEquals(i, length, builder);

break;

case "^Y":

builder.Append("Redo");

IsEquals(i, length, builder);

break;

case "^N":

builder.Append("New");

IsEquals(i, length, builder);

break;

default:

builder.Append(keys[i]);

IsEquals(i, length, builder);

break;

}

}

}

return builder.ToString();

}

void IsEquals(int i, int length, StringBuilder builder)

{

if(i<length)

builder.Append("+");

}

#region Public Method

public override string ToString()

{

return string.Format("Sendkeys to input data or operator with keys = ‘{0}‘",

this.KeysToString(Keys));

}

#endregion

}

}

如下代码为对应的WPF窗体XAML代码:


<Window x:Class="WpfApp.SendKeysInputData"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="SendKeysInputData" Height="250" Width="367">

<Grid>

<TextBox Margin="28,80,41,101" Name="textBox1">

<TextBox.ContextMenu>

<ContextMenu>

<MenuItem Header="Open" />

<MenuItem Header="Save" />

<MenuItem Header="New" />

<MenuItem Header="Save As" />

<MenuItem Header="Load" />

<MenuItem Header="ReLoad" />

</ContextMenu>

</TextBox.ContextMenu>

</TextBox>

</Grid>

</Window>

7.1.4 Summary

本小节首先简单介绍了如何在.NET通过调用Win32 API来模拟键盘的操作,进而通过实例演示了模拟鼠标与模拟键盘在基于UI Automation的自动化测试中应用。

使用UI Automation实现自动化测试--7.2 (模拟键盘复杂操作在自动化测试中的应用)

7.2 模拟键盘复杂操作

7.2.1 Introduction

键盘的操作相对比较灵活多变,在7.1节中我们通过System.Windows.Forms.SendKeys类中的方法来实现模拟按键。但它是一种模拟常规的敲击键盘,但有时候我们需要按下某个键而不松开,例如按住CTRL键多选操作等。在此种情况下我们需要同调用Win32 API中的keybd_event函数模拟对键盘上的某些键的Down和Up操作。

7.2.2 keybd_event Function

如下代码为模拟键盘操作的Win32 API函数:


/// <summary>

/// Simulate the keyboard operations

/// </summary>

/// <param name="bVk">Virtual key value</param>

/// <param name="bScan">Hardware scan code</param>

/// <param name="dwFlags">Action flag</param>

/// <param name="dwExtraInfo">Extension information which assication the keyborad action</param>

[DllImport("user32.dll")]

static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);

上表中的代码是通过调用Win32 API中的keybd_event函数模拟对键盘上的某些键的Down和Up操作。

其中参数列表对应的解释如下

static extern void keybd_event

(

byte bVk,//虚拟键值

byte bScan,//硬件扫描码

uint dwFlags,//动作标识

uint dwExtraInfo//与键盘动作关联的辅加信息

);


/// <summary>

/// Map virtual key

/// </summary>

/// <param name="uCode">Virtual key code</param>

/// <param name="uMapType">Defines the translation to be performed</param>

/// <returns></returns>

[DllImport("user32.dll")]

static extern byte MapVirtualKey(uint uCode, uint uMapType);

上表中的代码中函数将一虚拟键码翻译(映射)成一扫描码或一字符值,或者将一扫描码翻译成一虚拟键码。

参数详细解释如下

1)         uCode:定义一个键的扫描码或虚拟键码。该值如何解释依赖于uMapType参数的值。

2)         wMapType:定义将要执行的翻译。该参数的值依赖于uCode参数的值。取值如下:

a)         0:代表uCode是一虚拟键码且被翻译为一扫描码。若一虚拟键码不区分左右,则返回左键的扫描码。若未进行翻译,则函数返回O。

b)         1:代表uCode是一扫描码且被翻译为一虚拟键码,且此虚拟键码不区分左右。若未进行翻译,则函数返回0。

c)         2:代表uCode为一虚拟键码且被翻译为一未被移位的字符值存放于返回值的低序字中。死键(发音符号)则通过设置返回值的最高位来表示。若未进行翻译,则函数返回0。

d)         3:代表uCode为一扫描码且被翻译为区分左右键的一虚拟键码。若未进行翻译,则函数返回0。

返回值

返回值可以是一扫描码,或一虚拟键码,或一字符值,这完全依赖于不同的uCode和uMapType的值。若未进行翻译,则函数返回O。

下面我们通过模拟鼠标和键盘相结合的方式来达到多选ListBox中的项:


using System;

using System.Text;

using System.Diagnostics;

using System.Threading;

using System.Windows.Automation;

using System.Runtime.InteropServices;

using System.Windows;

using System.Windows.Forms;

namespace UIATest

{

class Program

{

static void Main(string[] args)

{

Process process = Process.Start(@"E:\WorkBook\ATP\WpfApp\bin\Debug\WpfApp.exe");

int processId = process.Id;

AutomationElement element = FindElementById(processId, "ListBox1");

AutomationElementCollection listItems = element.FindAll(TreeScope.Children,

new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem));

SendCtrlKey(true);

Thread.Sleep(500);

ClickLeftMouse(listItems[0]);

Thread.Sleep(500);

ClickLeftMouse(listItems[2]);

Thread.Sleep(500);

ClickLeftMouse(listItems[3]);

Thread.Sleep(500);

ClickLeftMouse(listItems[5]);

SendCtrlKey(false);

//Note: The statement ‘Thread.Sleep (500)‘ is to make each of these steps can be seen by tester.

Console.WriteLine("Test finised.");

}

#region Simulate keyboard

/// <summary>

/// Simulate the keyboard operations

/// </summary>

/// <param name="bVk">Virtual key value</param>

/// <param name="bScan">Hardware scan code</param>

/// <param name="dwFlags">Action flag</param>

/// <param name="dwExtraInfo">Extension information which assication the keyborad action</param>

[DllImport("user32.dll")]

static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);

/// <summary>

/// Map virtual key

/// </summary>

/// <param name="uCode">Virtual key code</param>

/// <param name="uMap">Defines the translation to be performed</param>

/// <returns></returns>

[DllImport("user32.dll")]

static extern byte MapVirtualKey(uint uCode, uint uMap);

public static void SendCtrlKey(bool isKeyDown)

{

if (!isKeyDown)

{

keybd_event(17, MapVirtualKey(17, 0), 0x2, 0);//Up CTRL key

}

else

{

keybd_event(17, MapVirtualKey(17, 0), 0, 0); //Down CTRL key

}

}

#endregion

#region ClickMouse

#region Import DLL

/// <summary>

/// Add mouse move event

/// </summary>

/// <param name="x">Move to specify x coordinate</param>

/// <param name="y">Move to specify y coordinate</param>

/// <returns></returns>

[DllImport("user32.dll")]

extern static bool SetCursorPos(int x, int y);

/// <summary>

/// Mouse click event

/// </summary>

/// <param name="mouseEventFlag">MouseEventFlag </param>

/// <param name="incrementX">X coordinate</param>

/// <param name="incrementY">Y coordinate</param>

/// <param name="data"></param>

/// <param name="extraInfo"></param>

[DllImport("user32.dll")]

extern static void mouse_event(int mouseEventFlag, int incrementX, int incrementY, uint data,UIntPtr extraInfo);

const int MOUSEEVENTF_MOVE = 0x0001;

const int MOUSEEVENTF_LEFTDOWN = 0x0002;

const int MOUSEEVENTF_LEFTUP = 0x0004;

const int MOUSEEVENTF_RIGHTDOWN = 0x0008;

const int MOUSEEVENTF_RIGHTUP = 0x0010;

const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;

const int MOUSEEVENTF_MIDDLEUP = 0x0040;

const int MOUSEEVENTF_ABSOLUTE = 0x8000;

#endregion

public static void ClickLeftMouse(AutomationElement element)

{

if (element == null)

{

throw new NullReferenceException(string.Format("Element with AutomationId ‘{0}‘ and Name ‘{1}‘ can not be find.",

element.Current.AutomationId, element.Current.Name));

}

Rect rect = element.Current.BoundingRectangle;

int IncrementX = (int)(rect.Left + rect.Width / 2);

int IncrementY = (int)(rect.Top + rect.Height / 2);

//Make the cursor position to the element.

SetCursorPos(IncrementX, IncrementY);

//Make the left mouse down and up.

mouse_event(MOUSEEVENTF_LEFTDOWN, IncrementX, IncrementY, 0, UIntPtr.Zero);

mouse_event(MOUSEEVENTF_LEFTUP, IncrementX, IncrementY, 0, UIntPtr.Zero);

}

#endregion

/// <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;

}

}

}

     对应的XAML代码如下:


<Window x:Class="WpfApp.ListBox"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="ListBox" Height="219" Width="349">

<Grid>

<ListBox SelectionMode="Extended" Name="ListBox1">

<ListBoxItem>ListBoxItem 1</ListBoxItem>

<ListBoxItem>ListBoxItem 2</ListBoxItem>

<ListBoxItem>ListBoxItem 3</ListBoxItem>

<ListBoxItem>ListBoxItem 4</ListBoxItem>

<ListBoxItem>ListBoxItem 5</ListBoxItem>

<ListBoxItem>ListBoxItem 6</ListBoxItem>

<ListBoxItem>ListBoxItem 7</ListBoxItem>

<ListBoxItem>ListBoxItem 8</ListBoxItem>

<ListBoxItem>ListBoxItem 9</ListBoxItem>

<ListBoxItem>ListBoxItem 10</ListBoxItem>

</ListBox>

</Grid>

</Window>

7.2.3 Summary

本小节首先简单介绍了如何在.NET通过调用Win32 API来模拟键盘的操作,进而通过实例演示了模拟鼠标与模拟键盘在基于UI Automation的自动化测试中应用。

时间: 2024-08-25 10:09:41

使用UI Automation实现自动化测试--5-7的相关文章

基于UI Automation的自动化测试框架 .

http://blog.csdn.net/roger_ge/article/details/5531941 第一部分:前言 自动化测试或许是众多测试同行都在研究或准备研究的领域.结合自己的能力和公司的状况,选择合适的自动化工具.搭建正确而又高效的框架或许是个永远讨论不完的话题,正如应了那句话,没有最好,只有更好. 个人所在的公司当前开展的很多项目都是基于Win7和WPF开发的,之前想尝试用QTP对之进行录制和回放操作,不幸的是,需要额外的WPF插件支持:另外QTP的脚本语言是VBScript,虽

使用UI Automation实现自动化测试 --工具使用

当前项目进行三个多月了,好久也没有写日志了:空下点时间,补写下N久没写的日志 介绍下两个工具 我本人正常使用的UISpy.exe工具和inspect.exe工具 这是UISPY工具使用的图,正常使用到的几个属性 这里重点说一下微软件的UI Automation中的重要类型是AutomationElement 图上的文本元素可通过AutomationElement,上级类型来获取子节点中的窗体或控件 ,也可以根据类型获取 如图所示:我们通过UIspy工具找到相应的控件名称,就可以用以下语法和属性获

使用UI Automation实现自动化测试 --微软提供的控件Pattern

微软提供的控件Pattern System.Windows.Automation 命名空间 System.Windows.Automation.BasePattern 为控件模式类提供基实现 System.Windows.Automation.DockPattern 表示在某个停靠容器内公开其停靠属性的控件 System.Windows.Automation.ExpandCollapsePattern 表示以可视方式进行展开(以显示内容)和折叠(以隐藏内容)的控件. System.Windows

UI Automation

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

iOS instruments之ui automation的简单使用(高手绕道)

最近使用了几次instruments中的automation工具,现记录下automation的简单使用方法,希望对没接触过自动化测试又有需求的人有所帮助.  UI 自动测试是iOS 中重要的附加功能,它由名为"Automation"的新的工具对象支持.Automation工具的脚本是用JavaScript语言编写,主要用于分析应用的性能和用户行为,模仿/击发被请求的事件,利用它可以完成对被测应用的简单的UI测试及相关功能测试. 一. 简单的录制脚本 打开xcode,这里用我为我家亲爱

关于UI Automation框架

微软提供的UI Automation框架给开发windows平台的自动化测试带来了很大的便利,这里就总结一下相关的代码. 首先,直接使用UI Automation框架,完成一个NotePad的about窗口中的 “OK” button的点击: 1 AutomationElement root = AutomationElement.RootElement; 2 AutomationElement about_notepad_windows = root.FindFirst( 3 TreeScop

Jacob.UIAutomation.dll (.NET UI Automation封装) - 绝对原创

  UIAutomation是微软从Windows Vista开始推出的一套全新UI自动化测试技术, 简称UIA.在最新的Windows SDK中,UIA和MSAA等其它支持UI自动化技术的组件放在一起发布,叫做Windows Automation API. UIA定义了全新的.针对UI自动化的接口和模式. 分别是支持对UI元素进行遍历和条件化查询的TreeWalker/FindAll.定义了读写UI元素属性的UIA Property, 包括Name. ID.Type.ClassName.Loc

Fitnesse + Powerslim + UI Automation框架下抽取UI操作方法的思路

我们的自动化测试框架的Fitnesse + Powerslim,一般情况下会尽量避免在UI层面的操作,但是有些时候没有对应的命令行接口只能用UI去测试. Powershell里面作UI测试用得比较多的库是UI Automation,但是Powershell的语句相对来说比较繁琐,例如: Get-UIAWindow -ProcessName abc -Seconds 10 | Get-UIATab | Get-UIATabItem -Name "General"|Invoke-UIATa

MS UI Automation简介

转自:http://blog.csdn.net/ffeiffei/article/details/6637418 MS UI Automation(Microsoft User Interface Automation:UIA)是随.net framework3.0一起发布的,虽然在如今这个几乎每天都有各种新名词.新技术出来的所谓的21世纪,它显得已经有些过时了.前些日子,正好一个项目,可以用到它,又重新整理了一下: 什么是MS UI Automation MS UI Automation是MS