軟件測試自動化之- 基於反射的UI自動化測試框架 - UI Automation Test Framework

測試自動化程序的任務


 

基於反射的ui測試自動化程序,要完成的6項任務:ide

  • 經過某種方式從測試套件程序中運行待測程序(AUT: Applicaton Under Test),以便於兩個程序之間進行通訊
  • 操縱應用程序的窗體,從而模擬用戶對窗體所實施的moving和resizing操做
  • 檢查應用程序窗體,肯定應用程序的狀態是否準確
  • 操縱應用程序控件的屬性,從而模擬用戶的一些操做,好比模擬在一個TextBox控件裏輸入字符
  • 檢查應用程序控件的屬性,肯定應用程序的狀態是否準確
  • 調用應用程序的方法,從而模擬一些用戶操做,好比模擬單擊一個按鈕

待測程序


AUT是一個剪刀、石頭、布的猜拳軟件,當點擊button1時,會在listbox中顯示誰是勝者。測試

圖1 待測程序GUIui

AUT代碼以下:this

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace AUTForm
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        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");
            }
        }

        private void menuItem2_Click(object sender, EventArgs e)
        {
            Application.Exit();
        } 
    }
}
View Code

測試程序


啓動待測程序

要使用反射技術經過UI來測試Windows窗體,必需要在測試套件所在的進程內建立一個單獨的線程來運行被測程序。這樣,測試程序和被測程序就會在運行在同一進程裏面,從而能夠相互進行通訊。spa

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Threading;
using System.Diagnostics;
using System.Drawing;

namespace AUTFormTest
{
    class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("\nStarting test scenario");
                Console.WriteLine("\nLaunching Form1");
                Form theForm = null;
                string formName = "AUTForm.Form1";
                string path = @"E:\wicresoft\Management\Knowledge\Learned\TestFramework\TestSampleLearn\AUTForm\bin\Debug\AUTForm.exe";
                theForm = LaunchApp(path, formName);

                Console.WriteLine("\nMoving Form1");
                Point pt = new Point(320, 100);
                Thread.Sleep(3000);
                SetFormProperty.SetFormPropertyValue(theForm, "Location", pt);

                Console.WriteLine("\nSetting textBox1 to 'rock'");
                Thread.Sleep(3000);
                SetControlProperty.SetControlPropertyValue(theForm, "textBox1", "Text", "rock");
                Console.WriteLine("Setting comboBox1 to 'scissors'");
                Thread.Sleep(3000);
                SetControlProperty.SetControlPropertyValue(theForm, "comboBox1", "Text",
                                        "scissors");

                Console.WriteLine("\nClicking button1");
                object[] parms = new object[] { null, EventArgs.Empty };
                Thread.Sleep(3000);
                InvokeClickMethod.InvokeMethod(theForm, "button1_Click", parms);

                bool pass = true;

                Console.WriteLine("\nChecking listBox1 for 'TextBox wins'");
                Thread.Sleep(3000); 
                ListBox.ObjectCollection oc =
                   (ListBox.ObjectCollection)
                      GetControlProperty.GetControlPropertyValue(theForm, "listBox1",
                                                       "Items");
                string s = oc[0].ToString();
                if (s.IndexOf("TextBox wins") == -1)
                    pass = false;

                if (pass)
                    Console.WriteLine("\n-- Scenario result = Pass --");
                else
                    Console.WriteLine("\n-- Scenario result = *FAIL* --");

                Console.WriteLine("\nClicking File->Exit in 3 seconds");
                Thread.Sleep(3000);
                InvokeClickMethod.InvokeMethod(theForm, "menuItem2_Click", parms);

                Console.WriteLine("\nEnd test scenario");                
            }
            catch (Exception ex)
            {
                Console.WriteLine("Fatal error: " + ex.Message);
            }
        }

        /// <summary>
        /// Lunch App 
        /// </summary>
        /// <param name="path">The Application path</param>
        /// <param name="formName">The Form name</param>
        /// <returns>Form Instance</returns>
        static Form LaunchApp(string path, string formName)
        {
            //1. Load assmebly
            //2. Get the define type
            //3. Create type instance
            //經過assembly讀取程序,而後程序獲取窗體類型,經過程序建立窗體的實例.
            Form result = null;
            Assembly a = Assembly.LoadFrom(path);
            Type t = a.GetType(formName);
            result = (Form)a.CreateInstance(t.FullName);

            AppState aps = new AppState(result);
            ThreadStart ts = new ThreadStart(aps.RunApp);
            Thread thread = new Thread(ts);
            //single thread
            thread.SetApartmentState(ApartmentState.STA);
            thread.IsBackground = true;

            thread.Start();
            return result;
        }
    }

    public class AppState
    {
        public readonly Form formToRun;
        public AppState(Form f)
        {
            this.formToRun = f;
        }

        public void RunApp()
        {
            Application.Run(formToRun);
        }
    }
}
View Code

設置窗體的屬性

    public static class SetFormProperty
    {
        delegate void SetFormPropertyValueHandler(Form f, string propertyName, object newValue);

        public static void SetFormPropertyValue(Form f, string propertyName, object newValue)
        {
            if (f.InvokeRequired)
            {
                Delegate d = new SetFormPropertyValueHandler(SetFormPropertyValue);
                object[] o = new object[] { f, propertyName, newValue };
                f.Invoke(d, o);
                return;
            }
            else
            {                
                Type t = f.GetType();
                PropertyInfo pi = t.GetProperty(propertyName);
                pi.SetValue(f, newValue, null);
            }
        }
    }
View Code

問題1:若是在測試程序中直接調用PropertyInfo.SetValue()會拋錯:"Exception has been thrown by the target of an invocation."。這是由於,不是在窗體的主線程裏調用,而是在自動化測試程序所建立的一個線程裏調用。所以,咱們用Form.Invoke()方法以間接的方式調用SetValue。間接的方式調用,就是用delegate對象調用SetValue()。線程

獲取窗體的屬性

 public static class GetFormProperty
    {
        delegate object GetFormPropertyValueHandler(Form f, string propertyName);
        public static object GetFormPropertyValue(Form f, string propertyName)
        {
            if (f.InvokeRequired)
            {
                Delegate d = new GetFormPropertyValueHandler(GetFormPropertyValue);
                object[] o = new object[] { f, propertyName };
                object iresult = f.Invoke(d, o);
                return iresult;
            }
            else
            {                
                Type t = f.GetType();
                PropertyInfo pi = t.GetProperty(propertyName);
                object result = pi.GetValue(f, null);
                return result;
            }
        }
    }
View Code

設置控件的屬性

public static class SetControlProperty
    {
        delegate void SetControlPropertyValueHandler(Form f,
            string controlName, string propertyName, object newValue);

        public static void SetControlPropertyValue(Form f, string controlName,
            string PropertyName, object newValue)
        {
            if (f.InvokeRequired)
            {
                Delegate d = new SetControlPropertyValueHandler(SetControlPropertyValue);
                object[] o = new object[] { f, controlName, PropertyName, newValue };
                f.Invoke(d, o);
            }
            else
            {                
                Type t1 = f.GetType();
                FieldInfo fi = t1.GetField(controlName, BindingFlagsList.Flags);
                object ctr1 = fi.GetValue(f);
                Type t2 = ctr1.GetType();
                PropertyInfo pi = t2.GetProperty(PropertyName);
                pi.SetValue(ctr1, newValue, null);
            }
        }
    }
View Code

BingFlags對象是用來過濾System.Reflection命名空間裏許多不一樣類型的方法的。定義以下:code

public static class BindingFlagsList
    {
        public static BindingFlags Flags = BindingFlags.Public |
            BindingFlags.NonPublic |
            BindingFlags.Static |
            BindingFlags.Instance;
    }
View Code

獲取控件的屬性

 public static class GetControlProperty
    {       
        delegate object GetControlPropertyValueHandler(Form f,
            string controlName, string propertyName);

        public static object GetControlPropertyValue(Form f, string controlName, string propertyName)
        {
            if (f.InvokeRequired)
            {
                Delegate d = new GetControlPropertyValueHandler(GetControlPropertyValue);
                object[] o = new object[] { f, controlName, propertyName };
                object iResult = f.Invoke(d, o);
                return iResult;
            }
            else
            {                
                Type t1 = f.GetType();
                FieldInfo fi = t1.GetField(controlName, BindingFlagsList.Flags);
                object ctr1 = fi.GetValue(f);
                Type t2 = ctr1.GetType();
                PropertyInfo pi = t2.GetProperty(propertyName);
                object gResult = pi.GetValue(ctr1, null);
                return gResult;
            }
        }
    }
View Code

方法調用

public static class InvokeClickMethod
    {
        static AutoResetEvent are = new AutoResetEvent(false); 
        delegate void InvokeMethodHandler(Form f, string methodName, params object[] parms);

        public static void InvokeMethod(Form f, string methodName, params object[] parms)
        {
            if (f.InvokeRequired)
            {
                Delegate d = new InvokeMethodHandler(InvokeMethod);
                f.Invoke(d, new object[] { f, methodName, parms });
                are.WaitOne();
            }
            else
            {                
                Type t = f.GetType();
                MethodInfo mi = t.GetMethod(methodName, BindingFlagsList.Flags);
                mi.Invoke(f, parms);
                are.Set();
            }
        }
    }
View Code

問題2:假如測試套件觸發了待測程序的某個方法,而這個方法直接或間接建立一個新的線程去執行。若是須要等新線程執行結束之後才能在測試套間裏繼續下一步操做:orm

  • 若是知道時間暫停長度,可使用Thread.Sleep().
  • 若是沒有辦法知道具體要暫停多長時間, 更好的解決方案可用AutoResetEvent對象來進行同步.  當須要暫停自動化測試程序的時候,就能夠插入are.WaitOne()語句, 這個語句會把AutoResetEvent對象的值設爲未設置.當前線程會暫停執行, 直到are.Set()語句把AutoResetEvent對象的值被設爲已設置.

結果


 

全部源代碼以下

http://pan.baidu.com/s/1cCSqE對象

相關文章
相關標籤/搜索