首页 > 代码库 > 《软件测试自动化之道》读书笔记 之 基于反射的UI测试

《软件测试自动化之道》读书笔记 之 基于反射的UI测试

《软件测试自动化之道》读书笔记 之 基于反射的UI测试

2014-09-24

测试自动化程序的任务
待测程序
测试程序
  启动待测程序
  设置窗体的属性
  获取窗体的属性
  设置控件的属性
  获取控件的属性
  方法调用
  待测程序代码

测试自动化程序的任务


 返回

基于反射的ui测试自动化程序,要完成的6项任务:

  • 通过某种方式从测试套件程序中运行待测程序(AUT: Applicaton Under Test),以便于两个程序之间进行通信
  • 操纵应用程序的窗体,从而模拟用户对窗体所实施的moving和resizing操作
  • 检查应用程序窗体,确定应用程序的状态是否准确
  • 操纵应用程序控件的属性,从而模拟用户的一些操作,比如模拟在一个TextBox控件里输入字符
  • 检查应用程序控件的属性,确定应用程序的状态是否准确
  • 调用应用程序的方法,从而模拟一些用户操作,比如模拟单击一个按钮

待测程序


 返回

AUT是一个剪刀、石头、布的猜拳软件,当点击button1时,会在listbox中显示谁是胜者。

图1 待测程序GUI

AUT代码如下:

 1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace AUT11 {12     public partial class Form1 : Form13     {14         public Form1()15         {16             InitializeComponent();17         }18 19         private void button1_Click(object sender, EventArgs e)20         {21             string tb = textBox1.Text;22             string cb = comboBox1.Text;23 24             if (tb == cb)25                 listBox1.Items.Add("Result is a tie");26             else if (tb == "paper" && cb == "rock" || tb == "rock" && cb == "scissors" || tb == "scissors" && cb == "paper")27                 listBox1.Items.Add("The TextBox wins");28             else29                 listBox1.Items.Add("the ComboBox wins");30         }31 32         private void Form1_Load(object sender, EventArgs e)33         {34             this.textBox1.Text = this.comboBox1.Items[1].ToString();35             this.comboBox1.Text = this.comboBox1.Items[0].ToString();36         }37 38         private void menuItem2_Click(object sender, EventArgs e)39         {40             this.Close();41         }42     }43 }
View Code

测试程序


 返回

启动待测程序

 1 using System; 2  3 using System.Reflection; 4 using System.Windows.Forms; 5 using System.Threading; 6  7  8         static void Main(string[] args) 9         {10                 string formName = "AUT.Form1";11                 string path = "..\\..\\..\\AUT\\bin\\Debug\\AUT.exe";12                 theForm = LaunchApp(path, formName);13 14                 //...15         } 16 17         static Form LaunchApp(string path, string formName)18         {19             Form result = null;20             Assembly a = Assembly.LoadFrom(path);21             Type t = a.GetType(formName);22             result = (Form)a.CreateInstance(t.FullName);23             AppState aps = new AppState(result);24             ThreadStart ts = new ThreadStart(aps.RunApp);25             Thread thread = new Thread(ts);26             thread.Start();27             return result;28         }29 30         private class AppState31         {32             public readonly Form formToRun;33             public AppState(Form f)34             {35                 this.formToRun = f;36             }37             public void RunApp()38             {39                 Application.Run(formToRun);40             }41         }
View Code

要使用反射技术通过UI来测试Windows窗体,必须要在测试套件所在的进程内创建一个单独的线程来运行被测程序。这样,测试程序和被测程序就会在运行在同一进程里面,从而可以相互进行通信。

设置窗体的属性

 1         static void Main(string[] args) 2         { 3                 //... 4                 //移动窗体到指定位置 5                 SetFormPropertyValue(theForm, "Location", pt); 6                 //... 7         } 8  9         delegate void SetFormPropertyValueHandler(Form f, string propertyName, object newValue);10         static void SetFormPropertyValue(Form f, string propertyName, object newValue)11         {12             //true if the control‘s System.Windows.Forms.Control.Handle was created on13             //     a different thread than the calling thread14             if (f.InvokeRequired)15             {16                 Delegate d = new SetFormPropertyValueHandler(SetFormPropertyValue);17                 object[] o = new object[] { f, propertyName, newValue };18                 f.Invoke(d, o);19                 are.WaitOne();20             }21             else22             {23                 Type t = f.GetType();24                 PropertyInfo pi = t.GetProperty(propertyName);25                 pi.SetValue(f, newValue, null);26                 are.Set();27             }28         }
View Code

问题1:如果在测试程序中直接调用PropertyInfo.SetValue()会抛错:"Exception has been thrown by the target of an invocation."。这是因为,不是在窗体的主线程里调用,而是在自动化测试程序所创建的一个线程里调用。因此,我们用Form.Invoke()方法以间接的方式调用SetValue。间接的方式调用,就是用delegate对象调用SetValue()。见如下代码:

1                 Delegate d = new SetFormPropertyValueHandler(SetFormPropertyValue);2                 object[] o = new object[] { f, propertyName, newValue };3                 //f type is Form, in this case, is instance of AUT.Form14                 f.Invoke(d, o);
View Code

问题2:假如测试套件触发了待测程序的某个方法,而这个方法直接或间接创建一个新的线程去执行。如果需要等新线程执行结束以后才能在测试套间里继续下一步操作,可用AutoResetEvent对象来进行同步。代码如下:

1         //在类的作用域内定义如下对象,false参数意味着把这个对象出示初始化为未设置2         static AutoResetEvent are = new AutoResetEvent(false);3 4         //这个语句把AutoResetEvent对象的值设为未设置,当前线程暂停执行,直到are.Set()语句把AutoResetEvent对象的值设为已设置5      are.WaitOne()
View Code

 

获取窗体的属性

 1         delegate object GetFormPropertyValueHandler(Form f, string propertyName); 2         static object GetFormPropertyValue(Form f, string propertyName) 3         { 4             if (f.InvokeRequired) 5             { 6                 Delegate d = new SetFormPropertyValueHandler(SetFormPropertyValue); 7                 object[] o = new object[] { f, propertyName }; 8                 object iResult = f.Invoke(d, o); 9                 are.WaitOne();10                 return iResult;11             }12             else13             {14                 Type t = f.GetType();15                 PropertyInfo pi = t.GetProperty(propertyName);16                 object gResult = pi.GetValue(f, null);17                 are.Set();18                 return gResult;19             }20         }
View Code

设置控件的属性

 1         static void Main(string[] args) 2         { 3                 //... 4                 SetControlPropertyValue(theForm, "textBox1", "Text", "rock"); 5                 //... 6         } 7  8         delegate void SetControlPropertyValueHandler(Form f, string controlName, string propertyName, object newValue); 9         static void SetControlPropertyValue(Form f, string controlName, string propertyName, object newValue)10         {11             if (f.InvokeRequired)12             {13                 Delegate d = new SetControlPropertyValueHandler(SetControlPropertyValue);14                 object[] o = new object[] { f, controlName, propertyName, newValue };15                 f.Invoke(d, o);16                 are.WaitOne();17             }18             else19             {20                 Type t1 = f.GetType();21                 FieldInfo fi = t1.GetField(controlName, flags);22                 object ctrl = fi.GetValue(f);23                 Type t2 = ctrl.GetType();24                 PropertyInfo pi = t2.GetProperty(propertyName);25                 pi.SetValue(ctrl, newValue, null);26                 are.Set();27             }28         }
View Code

BingFlags对象是用来过滤System.Reflection命名空间里许多不同类型的方法的。定义如下:

        static BindingFlags flags = BindingFlags.Public |                                    BindingFlags.NonPublic |                                    BindingFlags.Static |                                    BindingFlags.Instance;

 

获取控件的属性

 1         static void Main(string[] args) 2         { 3                 //... 4                 ListBox.ObjectCollection oc = (ListBox.ObjectCollection)GetControlPropertyValue(theForm, "listBox1", "Items"); 5                 string s = oc[0].ToString(); 6                 if (s.IndexOf("TextBox wins") == -1) 7                     pass = false; 8                 //... 9         }10 11         delegate object GetControlPropertyValueHandler(Form f, string controlName, string propertyName);12         static object GetControlPropertyValue(Form f, string controlName, string propertyName)13         {14             if (f.InvokeRequired)15             {16                 Delegate d = new GetControlPropertyValueHandler(GetControlPropertyValue);17                 object[] o = new object[] { f, controlName, propertyName };18                 object iResult = f.Invoke(d, o);19                 are.WaitOne();20                 return iResult;21             }22             else23             {24                 Type t1 = f.GetType();25                 FieldInfo fi = t1.GetField(controlName, flags);26                 object ctrl = fi.GetValue(f);27                 Type t2 = ctrl.GetType();28                 PropertyInfo pi = t2.GetProperty(propertyName);29                 object gResult = pi.GetValue(ctrl, null);30                 are.Set();31                 return gResult;32             }
View Code

方法调用

 1         static void Main(string[] args) 2         { 3                 //... 4                 object[] parms = new object[] { null, EventArgs.Empty }; 5                 InvokeMethod(theForm, "button1_Click", parms); 6                 //... 7         } 8  9         delegate void InvokeMethodHandler(Form f, string methodName, params object[] parms);10         static void InvokeMethod(Form f, string methodName, params object[] parms)11         {12             if (f.InvokeRequired)13             {14                 Delegate d = new InvokeMethodHandler(InvokeMethod);15                 f.Invoke(d, new object[] { f, methodName, parms });16                 are.WaitOne();17             }18             else19             {20                 Type t = f.GetType();21                 MethodInfo mi = t.GetMethod(methodName, flags);22                 mi.Invoke(f, parms);23                 are.Set();24             }25         }
View Code

待测程序代码

  1 // Chapter 2 - Reflection-Based UI Testing  2 // Example Program: ReflectionUITest  3   4 using System;  5 using System.Reflection;  6 using System.Windows.Forms;  7 using System.Threading;  8 using System.Drawing;  9  10 namespace ReflectionUITest 11 { 12     class Class1 13     { 14         static BindingFlags flags = BindingFlags.Public | 15                                     BindingFlags.NonPublic | 16                                     BindingFlags.Static | 17                                     BindingFlags.Instance; 18         static AutoResetEvent are = new AutoResetEvent(false); 19  20         [STAThread] 21         static void Main(string[] args) 22         { 23             try 24             { 25                 Console.WriteLine("\nStarting test scenario"); 26                 Console.WriteLine("\nLaunching Form1"); 27                 Form theForm = null; 28                 string formName = "AUT.Form1"; 29                 string path = "..\\..\\..\\AUT\\bin\\Debug\\AUT.exe"; 30                 theForm = LaunchApp(path, formName); 31  32                 System.Threading.Thread.Sleep(1000); 33  34                 Console.WriteLine("\nMoving Form1"); 35                 Point pt = new Point(320, 100); 36                 SetFormPropertyValue(theForm, "Location", pt); 37  38                 System.Threading.Thread.Sleep(1000); 39  40                 Console.WriteLine("\nSetting textBox1 to ‘rock‘"); 41                 SetControlPropertyValue(theForm, "textBox1", "Text", "rock"); 42                 Console.WriteLine("Setting comboBox1 to ‘scissors‘"); 43                 SetControlPropertyValue(theForm, "comboBox1", "Text", "scissors"); 44  45                 System.Threading.Thread.Sleep(1000); 46  47                 Console.WriteLine("\nClicking button1"); 48                 object[] parms = new object[] { null, EventArgs.Empty }; 49                 InvokeMethod(theForm, "button1_Click", parms); 50  51                 bool pass = true; 52  53                 Console.WriteLine("\nChecking listBox1 for ‘TextBox wins‘"); 54                 ListBox.ObjectCollection oc = (ListBox.ObjectCollection)GetControlPropertyValue(theForm, "listBox1", "Items"); 55                 string s = oc[0].ToString(); 56                 if (s.IndexOf("TextBox wins") == -1) 57                     pass = false; 58  59                 if (pass) 60                     Console.WriteLine("\n-- Scenario result = Pass --"); 61                 else 62                     Console.WriteLine("\n-- Scenario result = *FAIL* --"); 63  64                 Console.WriteLine("\nClicking File->Exit in 3 seconds"); 65                 Thread.Sleep(3000); 66                 InvokeMethod(theForm, "menuItem2_Click", parms); 67  68                 Console.WriteLine("\nEnd test scenario"); 69             } 70             catch (Exception ex) 71             { 72                 Console.WriteLine("Fatal error: " + ex.Message); 73             } 74             Console.Read(); 75         } // Main() 76  77         static Form LaunchApp(string path, string formName) 78         { 79             Form result = null; 80             Assembly a = Assembly.LoadFrom(path); 81             Type t = a.GetType(formName); 82             result = (Form)a.CreateInstance(t.FullName); 83             AppState aps = new AppState(result); 84             ThreadStart ts = new ThreadStart(aps.RunApp); 85             Thread thread = new Thread(ts); 86             thread.Start(); 87             return result; 88         } 89         private class AppState 90         { 91             public readonly Form formToRun; 92             public AppState(Form f) 93             { 94                 this.formToRun = f; 95             } 96             public void RunApp() 97             { 98                 Application.Run(formToRun); 99             }100         } // class AppState101 102         delegate void SetFormPropertyValueHandler(Form f, string propertyName, object newValue);103         static void SetFormPropertyValue(Form f, string propertyName, object newValue)104         {105             //true if the control‘s System.Windows.Forms.Control.Handle was created on106             //     a different thread than the calling thread107             if (f.InvokeRequired)108             {109                 Delegate d = new SetFormPropertyValueHandler(SetFormPropertyValue);110                 object[] o = new object[] { f, propertyName, newValue };111                 //f type is Form, in this case, is instance of AUT.Form1112                 f.Invoke(d, o);113                 are.WaitOne();114             }115             else116             {117                 Type t = f.GetType();118                 PropertyInfo pi = t.GetProperty(propertyName);119                 pi.SetValue(f, newValue, null);120                 are.Set();121             }122         }123 124         delegate object GetFormPropertyValueHandler(Form f, string propertyName);125         static object GetFormPropertyValue(Form f, string propertyName)126         {127             if (f.InvokeRequired)128             {129                 Delegate d = new SetFormPropertyValueHandler(SetFormPropertyValue);130                 object[] o = new object[] { f, propertyName };131                 object iResult = f.Invoke(d, o);132                 are.WaitOne();133                 return iResult;134             }135             else136             {137                 Type t = f.GetType();138                 PropertyInfo pi = t.GetProperty(propertyName);139                 object gResult = pi.GetValue(f, null);140                 are.Set();141                 return gResult;142             }143         }144 145         delegate void SetControlPropertyValueHandler(Form f, string controlName, string propertyName, object newValue);146         static void SetControlPropertyValue(Form f, string controlName, string propertyName, object newValue)147         {148             if (f.InvokeRequired)149             {150                 Delegate d = new SetControlPropertyValueHandler(SetControlPropertyValue);151                 object[] o = new object[] { f, controlName, propertyName, newValue };152                 f.Invoke(d, o);153                 are.WaitOne();154             }155             else156             {157                 Type t1 = f.GetType();158                 FieldInfo fi = t1.GetField(controlName, flags);159                 object ctrl = fi.GetValue(f);160                 Type t2 = ctrl.GetType();161                 PropertyInfo pi = t2.GetProperty(propertyName);162                 pi.SetValue(ctrl, newValue, null);163                 are.Set();164             }165         }166 167         delegate void InvokeMethodHandler(Form f, string methodName, params object[] parms);168         static void InvokeMethod(Form f, string methodName, params object[] parms)169         {170             if (f.InvokeRequired)171             {172                 Delegate d = new InvokeMethodHandler(InvokeMethod);173                 f.Invoke(d, new object[] { f, methodName, parms });174                 are.WaitOne();175             }176             else177             {178                 Type t = f.GetType();179                 MethodInfo mi = t.GetMethod(methodName, flags);180                 mi.Invoke(f, parms);181                 are.Set();182             }183         }184 185         delegate object GetControlPropertyValueHandler(Form f, string controlName, string propertyName);186         static object GetControlPropertyValue(Form f, string controlName, string propertyName)187         {188             if (f.InvokeRequired)189             {190                 Delegate d = new GetControlPropertyValueHandler(GetControlPropertyValue);191                 object[] o = new object[] { f, controlName, propertyName };192                 object iResult = f.Invoke(d, o);193                 are.WaitOne();194                 return iResult;195             }196             else197             {198                 Type t1 = f.GetType();199                 FieldInfo fi = t1.GetField(controlName, flags);200                 object ctrl = fi.GetValue(f);201                 Type t2 = ctrl.GetType();202                 PropertyInfo pi = t2.GetProperty(propertyName);203                 object gResult = pi.GetValue(ctrl, null);204                 are.Set();205                 return gResult;206             }207         }208     } // Class1209 } // ns
View Code

 

图2 测试结果

《软件测试自动化之道》读书笔记 之 基于反射的UI测试