在目前进行软件测试时,都或多或少的引入了自动化测试的概念,而且市面上也有好多软件自动化方面相关的工具,比如QTP,比如LoadRunner,但是这些工具要么售价不菲,要么对某些方面功能支持的不够全面,那么我们在引入软件自动化测试时,都需要考虑哪些方面呢?当然是最符合自己项目的工具最合适,同时费用也比较低,那么除了市面上这些商业软件外,还有没有哪些方法可以自己动手来做软件的自动化测试呢?答案是肯定的.
本文将介绍实现软件自动化测试其中一种测试方法------UIAutomation技术,当然,此种方法仅限于对.Net软件使用.
反射技术具体是什么原理,本文将不做任何介绍,大家可以去网上搜索一下,有很多这方面的文章介绍,本文只介绍如何使用UIAutomation技术进行.NET软件的UI界面的自动化测试.
废话少说,多做实事!(本文所有代码均在VS2008环境下测试通过)
一. 创建待测试程序
1. 启动VS2008,建立一个C#的WinForm工程,并将工程文件名命名为AUT(Application Under Test)
2. 创建的Form上的按钮布局,如下图所示
3. 一个菜单(包含一个以及菜单File以及一个二级菜单Exit),一个TextBox,一个ComboBox,一个Button以及一个ListBox
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.ComboBox comboBox1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.ListBox listBox1;
4. 给Button按钮添加一个消息响应函数
private void button1_Click(object sender, EventArgs e)
{
string tb = textBox1.Text;
string cb = comboBox1.Text;
if (tb == cb)
{
listBox1.Items.Add("Result is a tie");
}
else if (tb == "paper" && cb == "rock" ||
tb == "rock" && cb == "scissors" ||
tb == "scissors" && cb == "paper")
{
listBox1.Items.Add("The TextBox wins");
}
else
{
listBox1.Items.Add("The ComboBox wins");
}
}
5. 给Exit菜单添加一个消息响应函数
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Exit();
}
6.给ComboBox控件添加三个Item,分别为paper,rock,scissions
编译待测试程序,生成文件名为AUT.exe的待测试程序
二. 创建测试程序
1. 启动VS2008,创建一个C#控制台程序,并命名为AutomationUITest
2. 在引用中添加以下三个类: UIAutomationClient, UIAutomationClientsideProviders以及UIAutomationTypes
工程中添加以下using语句
using System;
using System.Windows.Automation;
using System.Threading;
using System.Diagnostics;
using System.IO;
4. 定义启动测试程序以及获取窗体句柄的函数
///<summary>
///根据传入的路径启动相应的可执行程序,并返回进程ID
///</summary>
public static Int32 StartExe(string strExePath)
{
if (null == strExePath)
{
return 0;
}
Process ps = Process.Start(strExePath);
Thread.Sleep(3000);
return ps.Id;
}
///<summary>
///根据进程ID,查找相应窗体,并返回窗体句柄
///</summary>
public static AutomationElement GetWindowHandle(Int32 pid, int iWaitSecond)
{
AutomationElement targetWindow = null;
int iWaitTime = 0;
try
{
Process ps = Process.GetProcessById(pid);
targetWindow = AutomationElement.FromHandle(ps.MainWindowHandle);
while (null == targetWindow)
{
if (iWaitTime > iWaitSecond)
{
break;
}
Thread.Sleep(500);
targetWindow = AutomationElement.FromHandle(ps.MainWindowHandle);
}
return targetWindow;
}
catch (System.Exception ex)
{
string msg = "没有找到指定的窗口,请确认窗口已经启动!";
throw new InvalidProgramException(msg, ex);
}
}
5. 定义操作TextBox的相关函数\
///<summary>
///根据窗口句柄以及TextEdit的AutomationID,返回TextEdit句柄
///</summary>
public static AutomationElement GetTextEditHandle(AutomationElement parentWindowHandle, string sAutomationID)
{
PropertyCondition condition = null;
PropertyCondition codEdit = null;
AndCondition andConditon = null;
AutomationElement targetEditHandle = null;
try
{
if (null == parentWindowHandle)
{
return null;
}
condition = new PropertyCondition(AutomationElement.AutomationIdProperty, sAutomationID);
codEdit = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit);
andConditon = new AndCondition(condition, codEdit);
targetEditHandle = parentWindowHandle.FindFirst(TreeScope.Children, andConditon);
return targetEditHandle;
}
catch (System.Exception ex)
{
string msg = "没有找到指定的TextEdit,请确认TextEdit的AutomationID是否正确!";
throw new InvalidProgramException(msg, ex);
}
}
///<summary>
///根据TextEdit句柄,在TextEdit内填写数据
///只能设置单行输入的TextEdit
///</summary>
public static bool SetTextEditData(AutomationElement TextEditHandle, string strData)
{
ValuePattern vpTextEdit = null;
if (!TextEditHandle.Current.IsEnabled)
{
throw new InvalidOperationException("The control is not enabled.\n\n");
}
if (!TextEditHandle.Current.IsKeyboardFocusable)
{
throw new InvalidOperationException("The control is not focusable.\n\n");
}
vpTextEdit = TextEditHandle.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
if (null == vpTextEdit)
{
return false;
}
if (vpTextEdit.Current.IsReadOnly)
{
throw new InvalidOperationException("The control is read-only.");
}
vpTextEdit.SetValue(strData);
return true;
}
6. 定义操作ComboBox的相关函数
///<summary>
///根据窗口句柄以及ComboBox控件AutomationID,返回该ComboBox控件句柄
///</summary>
public static AutomationElement GetComboBoxHandle(AutomationElement parentWindowHandle, string sAutomationID)
{
AutomationElement ComboBoxHandle = null;
PropertyCondition NameCondition = null;
PropertyCondition TypeCondition = null;
AndCondition andCondition = null;
if (null == parentWindowHandle || null == sAutomationID)
{
return null;
}
NameCondition = new PropertyCondition(AutomationElement.AutomationIdProperty, sAutomationID);
TypeCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ComboBox);
andCondition = new AndCondition(NameCondition, TypeCondition);
ComboBoxHandle = parentWindowHandle.FindFirst(TreeScope.Children, andCondition);
if (null == ComboBoxHandle)
{
return null;
}
return ComboBoxHandle;
}
///<summary>
///根据ComboBox控件句柄,设置数据
///</summary>
public static bool SetComboBoxItemData(AutomationElement ComboBoxHandle, string strData)
{
AutomationElement TextEditHandle = null;
PropertyCondition TypeCondition = null;
ValuePattern vpTextPattern = null;
if (null == ComboBoxHandle || null == strData)
{
return false;
}
TypeCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit);
TextEditHandle = ComboBoxHandle.FindFirst(TreeScope.Children, TypeCondition);
if (null == TextEditHandle)
{
return false;
}
if (!TextEditHandle.Current.IsEnabled)
{
throw new InvalidOperationException("The control is not enabled.\n\n");
}
if (!TextEditHandle.Current.IsKeyboardFocusable)
{
throw new InvalidOperationException("The control is not focusable.\n\n");
}
vpTextPattern = TextEditHandle.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
if (null == vpTextPattern)
{
return false;
}
if (vpTextPattern.Current.IsReadOnly)
{
throw new InvalidOperationException("The control is read-only.");
}
vpTextPattern.SetValue(strData);
return true;
}
7. 定义操作Button的相关函数
///<summary>
///根据窗口句柄以及Button的AutomationID,返回Button的句柄
///</summary>
public static AutomationElement GetButtonHandle(AutomationElement parentWindowHandle, string sAutomationID)
{
PropertyCondition condition = null;
PropertyCondition codButton = null;
AndCondition andConditon = null;
AutomationElement targetButtonHandle = null;
try
{
if (null == parentWindowHandle)
{
return null;
}
condition = new PropertyCondition(AutomationElement.AutomationIdProperty, sAutomationID);
codButton = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button);
andConditon = new AndCondition(condition, codButton);
targetButtonHandle = parentWindowHandle.FindFirst(TreeScope.Children, andConditon);
return targetButtonHandle;
}
catch (System.Exception ex)
{
string msg = "没有找到指定的按钮,请确认按钮AutomationID是否正确!";
throw new InvalidProgramException(msg, ex);
}
}
///<summart>
///根据Button按钮句柄,进行鼠标左键单击
///</summary>
public static bool ButtonLeftClick(AutomationElement ButtonHandle)
{
object objButton = null;
InvokePattern ivkpButton = null;
try
{
if (null == ButtonHandle)
{
return false;
}
if (!ButtonHandle.TryGetCurrentPattern(InvokePattern.Pattern, out objButton))
{
return false;
}
ivkpButton = (InvokePattern)objButton;
ivkpButton.Invoke();
return true;
}
catch (System.Exception ex)
{
string msg = "鼠标左键单击失败!";
throw new InvalidProgramException(msg, ex);
}
}
8. 定义操作ListBox的相关函数
///<summary>
///根据窗口句柄以及ListBox控件AutomationID,返回该ListBox控件句柄
///</summary>
public static AutomationElement GetListBoxHandle(AutomationElement parentWindowHandle, string sAutomationID)
{
AutomationElement ListBoxHandle = null;
PropertyCondition NameCondition = null;
PropertyCondition TypeCondition = null;
AndCondition andCondition = null;
if (null == parentWindowHandle || null == sAutomationID)
{
return null;
}
NameCondition = new PropertyCondition(AutomationElement.AutomationIdProperty, sAutomationID);
TypeCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.List);
andCondition = new AndCondition(NameCondition, TypeCondition);
ListBoxHandle = parentWindowHandle.FindFirst(TreeScope.Children, andCondition);
if (null == ListBoxHandle)
{
return null;
}
return ListBoxHandle;
}
///<summary>
///根据ListBox控件句柄以及ItemCount,选择该Item
///</summary>
public static string GetListBoxItemName(AutomationElement ListBoxHandle, int iItemCount)
{
AutomationElementCollection ListBoxHandleCollection = null;
PropertyCondition TypeCondition = null;
if (null == ListBoxHandle || 0 > iItemCount)
{
return null;
}
TypeCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ListItem);
ListBoxHandleCollection = ListBoxHandle.FindAll(TreeScope.Children, TypeCondition);
if (null == ListBoxHandleCollection)
{
return null;
}
if (iItemCount >= ListBoxHandleCollection.Count)
{
return null;
}
return ListBoxHandleCollection[iItemCount].Current.Name;
}
9. 定义操作菜单的相关函数
///<summary>
///根据窗口句柄,以及MenuBar控件AutomationID,返回该MenuBar控件句柄
///</summary>
public static AutomationElement GetMenuBarHandle(AutomationElement parentWindow, string sAutomationID)
{
AutomationElement MBHandle = null;
PropertyCondition NameCondition = null;
PropertyCondition TypeCondition = null;
AndCondition andCondition = null;
if (null == parentWindow || null == sAutomationID)
{
return null;
}
NameCondition = new PropertyCondition(AutomationElement.AutomationIdProperty, sAutomationID);
TypeCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.MenuBar);
andCondition = new AndCondition(NameCondition, TypeCondition);
MBHandle = parentWindow.FindFirst(TreeScope.Children, andCondition);
if (null == MBHandle)
{
return null;
}
return MBHandle;
}
///<summary>
///根据MenuBar控件句柄以及一级菜单名称,弹出一级菜单
///</summary>
private static AutomationElement PopupMenuBarItem(AutomationElement MBHandle, string FirMenuTitle)
{
InvokePattern IPMenu = null;
AutomationElement MenuHandle = null;
PropertyCondition NameCondition = null;
PropertyCondition TypeCondition = null;
AndCondition andCondition = null;
if (null == MBHandle || null == FirMenuTitle)
{
return null;
}
NameCondition = new PropertyCondition(AutomationElement.NameProperty, FirMenuTitle);
TypeCondition = new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.MenuItem);
andCondition = new AndCondition(NameCondition, TypeCondition);
MenuHandle = MBHandle.FindFirst(TreeScope.Children, andCondition);
if (null == MenuHandle)
{
return null;
}
IPMenu = MenuHandle.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
if (null == IPMenu)
{
return null;
}
IPMenu.Invoke();
return MenuHandle;
}
///<summary>
///根据MenuBar控件句柄以及传入的菜单名称,选择相应的菜单
///传入的菜单名称,必须以空字符串结束
///</summary>
public static bool SelectMenuBarItem(AutomationElement MBHandle, string[] strTitle)
{
AutomationElement MenuItemHandle = null;
MenuItemHandle = MBHandle;
foreach (string str in strTitle)
{
if ("" == str)
{
break;
}
MenuItemHandle = PopupMenuBarItem(MenuItemHandle, str);
if (null == MenuItemHandle)
{
return false;
}
Thread.Sleep(400);
}
return true;
}
10. 添加测试代码
static void Main(string[] args)
{
string path = Directory.GetCurrentDirectory() + "\\AUT.exe";
try
{
Console.WriteLine("\nStart Application Under Test Exe");
Int32 ProcessId = StartExe(path);
Console.WriteLine("Application Under Test ProcessID: " + ProcessId.ToString());
Console.WriteLine("\nGet Main Window Handle");
AutomationElement mwh = GetWindowHandle(ProcessId, 3000);
Console.WriteLine("Main Window Handle: " + mwh.Current.NativeWindowHandle.ToString());
Console.WriteLine("\nGet TextBox Handle");
AutomationElement tbh = GetTextEditHandle(mwh, "textBox1");
Console.WriteLine("TextBox Handle: " + tbh.Current.NativeWindowHandle.ToString());
Console.WriteLine("\nGet ComboBox Handle");
AutomationElement cbh = GetComboBoxHandle(mwh, "comboBox1");
Console.WriteLine("ComboBox Handle: " + cbh.Current.NativeWindowHandle.ToString());
Console.WriteLine("\nGet Button Handle");
AutomationElement bth = GetButtonHandle(mwh, "button1");
Console.WriteLine("Button Handle: " + bth.Current.NativeWindowHandle.ToString());
Console.WriteLine("\nGet ListBox Handle");
AutomationElement lbh = GetListBoxHandle(mwh, "listBox1");
Console.WriteLine("ListBox Handle: " + lbh.Current.NativeWindowHandle.ToString());
Console.WriteLine("\nSet TextBox Value");
SetTextEditData(tbh, "paper");
Console.WriteLine("\nSet ConboxBox Value");
SetComboBoxItemData(cbh, "rock");
Console.WriteLine("\nPush Mouse Left button");
ButtonLeftClick(bth);
string result = GetListBoxItemName(lbh, 0);
if (result == "The TextBox wins")
{
Console.WriteLine("\nTest seceions = PASS");
}
else
{
Console.WriteLine("\nTest seceions = Failed");
}
Console.WriteLine("\nGet Menu Handle");
AutomationElement mbh = GetMenuBarHandle(mwh, "menuStrip1");
Console.WriteLine("Menu Handle: " + mbh.Current.NativeWindowHandle.ToString());
string[] strTitle = { "File", "Exit", "" };
Console.WriteLine("\nClose Application Under Test in 3 Seconds....");
Thread.Sleep(3000);
SelectMenuBarItem(mbh, strTitle);
Console.ReadLine();
}
catch (System.Exception ex)
{
Console.WriteLine("\nError msg: " + ex.Message);
}
}
11. 编译测试程序,OK,大功告成