标签:
基于反射的ui测试自动化程序,要完成的6项任务:
AUT是一个剪刀、石头、布的猜拳软件,当点击button1时,会在listbox中显示谁是胜者。
图1 待测程序GUI
AUT代码如下:
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(); } } }
要使用反射技术通过UI来测试Windows窗体,必须要在测试套件所在的进程内创建一个单独的线程来运行被测程序。这样,测试程序和被测程序就会在运行在同一进程里面,从而可以相互进行通信。
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); } } }
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); } } }
问题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; } } }
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); } } }
BingFlags对象是用来过滤System.Reflection命名空间里许多不同类型的方法的。定义如下:
public static class BindingFlagsList { public static BindingFlags Flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; }
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; } } }
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(); } } }
问题2:假如测试套件触发了待测程序的某个方法,而这个方法直接或间接创建一个新的线程去执行。如果需要等新线程执行结束以后才能在测试套间里继续下一步操作:
http://pan.baidu.com/s/1cCSqE
《软件测试自动化之道》- 基于反射的UI自动化测试框架 - UI Automation Test Framework
标签:
原文地址:http://www.cnblogs.com/binyao/p/5002791.html