《软件测试自动化之道》读书笔记 之 基于反射的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 AUT 11 { 12 public partial class Form1 : Form 13 { 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 else 29 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 }
测试程序
返回
启动待测程序
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 AppState 31 { 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 }
要使用反射技术通过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 on 13 // a different thread than the calling thread 14 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 else 22 { 23 Type t = f.GetType(); 24 PropertyInfo pi = t.GetProperty(propertyName); 25 pi.SetValue(f, newValue, null); 26 are.Set(); 27 } 28 }
问题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.Form1 4 f.Invoke(d, o);
问题2:假如测试套件触发了待测程序的某个方法,而这个方法直接或间接创建一个新的线程去执行。如果需要等新线程执行结束以后才能在测试套间里继续下一步操作,可用AutoResetEvent对象来进行同步。代码如下:
1 //在类的作用域内定义如下对象,false参数意味着把这个对象出示初始化为未设置 2 static AutoResetEvent are = new AutoResetEvent(false); 3 4 //这个语句把AutoResetEvent对象的值设为未设置,当前线程暂停执行,直到are.Set()语句把AutoResetEvent对象的值设为已设置 5 are.WaitOne()
获取窗体的属性
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 else 13 { 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 }
设置控件的属性
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 else 19 { 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 }
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 else 23 { 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 }
方法调用
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 else 19 { 20 Type t = f.GetType(); 21 MethodInfo mi = t.GetMethod(methodName, flags); 22 mi.Invoke(f, parms); 23 are.Set(); 24 } 25 }
待测程序代码
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 AppState 101 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 on 106 // a different thread than the calling thread 107 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.Form1 112 f.Invoke(d, o); 113 are.WaitOne(); 114 } 115 else 116 { 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 else 136 { 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 else 156 { 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 else 177 { 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 else 197 { 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 } // Class1 209 } // ns
图2 测试结果
时间: 2024-10-15 01:04:34