《软件测试自动化之道》读书笔记 之 基于反射的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 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

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

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

<软件测试自动化之道>读书笔记 之 基于Windows的UI测试 2014-09-25 测试自动化程序的任务待测程序测试程序  启动待测程序  获得待测程序主窗体的句柄  获得有名字控件的句柄  获得无名字控件的句柄  发送字符给控件  鼠标单击一个控件  处理消息对话框  处理菜单  检查应用程序状态  示例程序参考 本章主要讲述如何使用底层的Windows自动化技术通过用户界面来测试应用程序.这些技术涉及Win32 API的调用(比如FindWindow()函数)以及想待测程序发送Wind

《软件测试自动化之道》读书笔记 之 底层的Web UI 测试

<软件测试自动化之道>读书笔记 之 底层的Web UI 测试 2014-09-28 测试自动化程序的任务待测程序测试程序  启动IE并连接到这个实例  如何判断待测web程序完全加载到浏览器  操纵并检查IE Shell  操作待测Web页面上的HTML元素的值  验证Web页面上HTML元素  示例代码 测试自动化程序的任务 底层技术的核心是,通过直接调用mshtml.dll和shdocvw.dll库来访问并且操纵IE客户区域的HTML对象. 待测程序 新建一个网站“WebAUT”,删除原来

《软件测试自动化之道》读书笔记 之 目录导航

<软件测试自动化之道>读书笔记 之 目录导航 2014-10-09 源代码 第1章 API测试第2章 基于反射的UI测试第3章 基于Windows的UI测试第4章 测试套件设计模式第5章 请求-响应测试 第6章 基于脚本的Web UI测试第7章 底层的Web UI测试第8章 Web Service测试第9章 SQL存储过程测试 第10章 排列与组合第11章 ADO.NET测试第12章 XML测试

《软件测试自动化之道》- 基于反射的UI自动化测试框架 - UI Automation Test Framework

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

《软件测试自动化之道》读书笔记 之 SQL 存储过程测试

<软件测试自动化之道>读书笔记 之 SQL 存储过程测试 2014-09-28 待测程序测试程序   创建测试用例以及测试结果存储  执行T-SQL脚本  使用BCP工具导入测试用例数据  创建T-SQL 测试套件  当待测存储过程返回行集的时候,如何判断测试结果是否通过  当待测存储过程返回out参数时,如何判断测试结果是否通过  当待测存储过程没有返回值时,如何判断测试结果是否通过 许多基于Windows的系统都使用了SQL Server作为后台组件.待测程序经常通过存储过程来访问数据库.

《软件测试自动化之道》读书笔记 之 XML测试

<软件测试自动化之道>读书笔记 之 XML测试 2014-10-07 待测程序测试程序  通过XmlTextReader解析XML  通过XmlDocument解析XML  通过XmlPathDocument解析XML  通过XmlSerializer解析XML  通过DataSet解析XML   通过XSD Schema对XML文件进行验证  通过XSLT对XML文件进行修改  通过XmlTextWrite对XML文件进行写操作  比较两个XML文件是否严格相等  不考虑编码方式,比较两个X

《赢在测试2-中国软件测试专家访谈录》读书笔记

<赢在测试2-中国软件测试专家访谈录>读书笔记 2015-04-30 测试人物经历与观点  1.董杰 百度测试架构师    董杰的职业发展经历    如何成长为一个优秀的测试工程师?    如何开展软件测试工作?  2.邰晓梅 独立测试咨询顾问    邰晓梅职业发展经历    测试与开发的关系    对测试认识的三个阶段  3.一些分析:对测试的一些想法 职业发展是一个探索和尝试的过程,职业发展的目标是动态的,也可能会变. 测试人物经历与观点 返回 1.董杰 百度测试架构师 董杰的职业发展经历

VC++编程之道读书笔记(2)

第三篇 技术细节 第七章:细说开发人员必知必会的39个开发细节 细节36:单例模式的应用 在开发程序时,往往需要在整个工程中只需要一个类的实例.而这个实例一旦被创建就不能被其他的实例再创建了,通常我们称这个实现过程为单例模式. 既然要保证类只有一个实例,那么就需要其他的类不能使用实例化该类.因此,需要将其构造方法设为私有的,即使用private关键字修饰.同时,类中提供一个静态方法,该方法的返回值是该类的一个实例.这样就只能使用该静态方法来获取类的实例了,从而保证了唯一性. 下面通过具体代码来实

VC++编程之道读书笔记

第二篇 缪误21:位图数据是按照红绿蓝顺序存储的 大家都知道位图的颜色是由红.绿.蓝三个分量构成的,但是位图颜色数据存储的方式则不是按照这个顺序存储的,而是按照蓝.绿.红的顺序存储的.并且对于真彩色位图来说,位图的颜色数据是倒序存储的,即位图的第一行数据位于位图数据的最底部. 第三篇 细节12 :内存中的数组 在C++中通过数组可以操作内存,创建数组时需要为数组分配内存空间,操作数组时就是对内存空间中的数组元素进行操作.数组创建后,数组引用和数组元素分别存储在栈内存和堆内存中,并通过数组引用与数