在目前进行软件测试时,都或多或少的引入了自动化测试的概念,而且市面上也有好多软件自动化方面相关的工具,比如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,大功告成