《软件测试自动化之道》读书笔记 之 基于反射的UI测试
《软件测试自动化之道》读书笔记 之 基于反射的UI测试
2014-09-24
测试自动化程序的任务
待测程序
测试程序
启动待测程序
设置窗体的属性
获取窗体的属性
设置控件的属性
获取控件的属性
方法调用
测试程序代码
测试自动化程序的任务
基于反射的ui测试自动化程序,要完成的6项任务:
- 通过某种方式从测试套件程序中运行待测程序(AUT: Applicaton Under Test),以便于两个程序之间进行通信
- 操纵应用程序的窗体,从而模拟用户对窗体所实施的moving和resizing操作
- 检查应用程序窗体,确定应用程序的状态是否准确
- 操纵应用程序控件的属性,从而模拟用户的一些操作,比如模拟在一个TextBox控件里输入字符
- 检查应用程序控件的属性,确定应用程序的状态是否准确
- 调用应用程序的方法,从而模拟一些用户操作,比如模拟单击一个按钮
待测程序
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 AUT
{
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 Form1_Load(object sender, EventArgs e)
{
this.textBox1.Text = this.comboBox1.Items[].ToString();
this.comboBox1.Text = this.comboBox1.Items[].ToString();
} private void menuItem2_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
测试程序
启动待测程序
using System; using System.Reflection;
using System.Windows.Forms;
using System.Threading; static void Main(string[] args)
{
string formName = "AUT.Form1";
string path = "..\\..\\..\\AUT\\bin\\Debug\\AUT.exe";
theForm = LaunchApp(path, formName); //...
} static Form LaunchApp(string path, string formName)
{
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);
thread.Start();
return result;
} private class AppState
{
public readonly Form formToRun;
public AppState(Form f)
{
this.formToRun = f;
}
public void RunApp()
{
Application.Run(formToRun);
}
}
要使用反射技术通过UI来测试Windows窗体,必须要在测试套件所在的进程内创建一个单独的线程来运行被测程序。这样,测试程序和被测程序就会在运行在同一进程里面,从而可以相互进行通信。
设置窗体的属性
static void Main(string[] args)
{
//...
//移动窗体到指定位置
SetFormPropertyValue(theForm, "Location", pt);
//...
} delegate void SetFormPropertyValueHandler(Form f, string propertyName, object newValue);
static void SetFormPropertyValue(Form f, string propertyName, object newValue)
{
//true if the control's System.Windows.Forms.Control.Handle was created on
// a different thread than the calling thread
if (f.InvokeRequired)
{
Delegate d = new SetFormPropertyValueHandler(SetFormPropertyValue);
object[] o = new object[] { f, propertyName, newValue };
f.Invoke(d, o);
are.WaitOne();
}
else
{
Type t = f.GetType();
PropertyInfo pi = t.GetProperty(propertyName);
pi.SetValue(f, newValue, null);
are.Set();
}
}
问题1:如果在测试程序中直接调用PropertyInfo.SetValue()会抛错:"Exception has been thrown by the target of an invocation."。这是因为,不是在窗体的主线程里调用,而是在自动化测试程序所创建的一个线程里调用。因此,我们用Form.Invoke()方法以间接的方式调用SetValue。间接的方式调用,就是用delegate对象调用SetValue()。见如下代码:
Delegate d = new SetFormPropertyValueHandler(SetFormPropertyValue);
object[] o = new object[] { f, propertyName, newValue };
//f type is Form, in this case, is instance of AUT.Form1
f.Invoke(d, o);
问题2:假如测试套件触发了待测程序的某个方法,而这个方法直接或间接创建一个新的线程去执行。如果需要等新线程执行结束以后才能在测试套间里继续下一步操作,可用AutoResetEvent对象来进行同步。代码如下:
//在类的作用域内定义如下对象,false参数意味着把这个对象出示初始化为未设置
static AutoResetEvent are = new AutoResetEvent(false); //这个语句把AutoResetEvent对象的值设为未设置,当前线程暂停执行,直到are.Set()语句把AutoResetEvent对象的值设为已设置
are.WaitOne()
获取窗体的属性
delegate object GetFormPropertyValueHandler(Form f, string propertyName);
static object GetFormPropertyValue(Form f, string propertyName)
{
if (f.InvokeRequired)
{
Delegate d = new SetFormPropertyValueHandler(SetFormPropertyValue);
object[] o = new object[] { f, propertyName };
object iResult = f.Invoke(d, o);
are.WaitOne();
return iResult;
}
else
{
Type t = f.GetType();
PropertyInfo pi = t.GetProperty(propertyName);
object gResult = pi.GetValue(f, null);
are.Set();
return gResult;
}
}
设置控件的属性
static void Main(string[] args)
{
//...
SetControlPropertyValue(theForm, "textBox1", "Text", "rock");
//...
} delegate void SetControlPropertyValueHandler(Form f, string controlName, string propertyName, object newValue);
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);
are.WaitOne();
}
else
{
Type t1 = f.GetType();
FieldInfo fi = t1.GetField(controlName, flags);
object ctrl = fi.GetValue(f);
Type t2 = ctrl.GetType();
PropertyInfo pi = t2.GetProperty(propertyName);
pi.SetValue(ctrl, newValue, null);
are.Set();
}
}
BingFlags对象是用来过滤System.Reflection命名空间里许多不同类型的方法的。定义如下:
static BindingFlags flags = BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Static |
BindingFlags.Instance;
获取控件的属性
static void Main(string[] args)
{
//...
ListBox.ObjectCollection oc = (ListBox.ObjectCollection)GetControlPropertyValue(theForm, "listBox1", "Items");
string s = oc[].ToString();
if (s.IndexOf("TextBox wins") == -)
pass = false;
//...
} delegate object GetControlPropertyValueHandler(Form f, string controlName, string propertyName);
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);
are.WaitOne();
return iResult;
}
else
{
Type t1 = f.GetType();
FieldInfo fi = t1.GetField(controlName, flags);
object ctrl = fi.GetValue(f);
Type t2 = ctrl.GetType();
PropertyInfo pi = t2.GetProperty(propertyName);
object gResult = pi.GetValue(ctrl, null);
are.Set();
return gResult;
}
方法调用
static void Main(string[] args)
{
//...
object[] parms = new object[] { null, EventArgs.Empty };
InvokeMethod(theForm, "button1_Click", parms);
//...
} delegate void InvokeMethodHandler(Form f, string methodName, params object[] parms);
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, flags);
mi.Invoke(f, parms);
are.Set();
}
}
测试程序代码
// Chapter 2 - Reflection-Based UI Testing
// Example Program: ReflectionUITest using System;
using System.Reflection;
using System.Windows.Forms;
using System.Threading;
using System.Drawing; namespace ReflectionUITest
{
class Class1
{
static BindingFlags flags = BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Static |
BindingFlags.Instance;
static AutoResetEvent are = new AutoResetEvent(false); [STAThread]
static void Main(string[] args)
{
try
{
Console.WriteLine("\nStarting test scenario");
Console.WriteLine("\nLaunching Form1");
Form theForm = null;
string formName = "AUT.Form1";
string path = "..\\..\\..\\AUT\\bin\\Debug\\AUT.exe";
theForm = LaunchApp(path, formName); System.Threading.Thread.Sleep(); Console.WriteLine("\nMoving Form1");
Point pt = new Point(, );
SetFormPropertyValue(theForm, "Location", pt); System.Threading.Thread.Sleep(); Console.WriteLine("\nSetting textBox1 to 'rock'");
SetControlPropertyValue(theForm, "textBox1", "Text", "rock");
Console.WriteLine("Setting comboBox1 to 'scissors'");
SetControlPropertyValue(theForm, "comboBox1", "Text", "scissors"); System.Threading.Thread.Sleep(); Console.WriteLine("\nClicking button1");
object[] parms = new object[] { null, EventArgs.Empty };
InvokeMethod(theForm, "button1_Click", parms); bool pass = true; Console.WriteLine("\nChecking listBox1 for 'TextBox wins'");
ListBox.ObjectCollection oc = (ListBox.ObjectCollection)GetControlPropertyValue(theForm, "listBox1", "Items");
string s = oc[].ToString();
if (s.IndexOf("TextBox wins") == -)
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();
InvokeMethod(theForm, "menuItem2_Click", parms); Console.WriteLine("\nEnd test scenario");
}
catch (Exception ex)
{
Console.WriteLine("Fatal error: " + ex.Message);
}
Console.Read();
} // Main() static Form LaunchApp(string path, string formName)
{
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);
thread.Start();
return result;
}
private class AppState
{
public readonly Form formToRun;
public AppState(Form f)
{
this.formToRun = f;
}
public void RunApp()
{
Application.Run(formToRun);
}
} // class AppState delegate void SetFormPropertyValueHandler(Form f, string propertyName, object newValue);
static void SetFormPropertyValue(Form f, string propertyName, object newValue)
{
//true if the control's System.Windows.Forms.Control.Handle was created on
// a different thread than the calling thread
if (f.InvokeRequired)
{
Delegate d = new SetFormPropertyValueHandler(SetFormPropertyValue);
object[] o = new object[] { f, propertyName, newValue };
//f type is Form, in this case, is instance of AUT.Form1
f.Invoke(d, o);
are.WaitOne();
}
else
{
Type t = f.GetType();
PropertyInfo pi = t.GetProperty(propertyName);
pi.SetValue(f, newValue, null);
are.Set();
}
} delegate object GetFormPropertyValueHandler(Form f, string propertyName);
static object GetFormPropertyValue(Form f, string propertyName)
{
if (f.InvokeRequired)
{
Delegate d = new SetFormPropertyValueHandler(SetFormPropertyValue);
object[] o = new object[] { f, propertyName };
object iResult = f.Invoke(d, o);
are.WaitOne();
return iResult;
}
else
{
Type t = f.GetType();
PropertyInfo pi = t.GetProperty(propertyName);
object gResult = pi.GetValue(f, null);
are.Set();
return gResult;
}
} delegate void SetControlPropertyValueHandler(Form f, string controlName, string propertyName, object newValue);
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);
are.WaitOne();
}
else
{
Type t1 = f.GetType();
FieldInfo fi = t1.GetField(controlName, flags);
object ctrl = fi.GetValue(f);
Type t2 = ctrl.GetType();
PropertyInfo pi = t2.GetProperty(propertyName);
pi.SetValue(ctrl, newValue, null);
are.Set();
}
} delegate void InvokeMethodHandler(Form f, string methodName, params object[] parms);
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, flags);
mi.Invoke(f, parms);
are.Set();
}
} delegate object GetControlPropertyValueHandler(Form f, string controlName, string propertyName);
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);
are.WaitOne();
return iResult;
}
else
{
Type t1 = f.GetType();
FieldInfo fi = t1.GetField(controlName, flags);
object ctrl = fi.GetValue(f);
Type t2 = ctrl.GetType();
PropertyInfo pi = t2.GetProperty(propertyName);
object gResult = pi.GetValue(ctrl, null);
are.Set();
return gResult;
}
}
} // Class1
} // ns
图2 测试结果
《软件测试自动化之道》读书笔记 之 基于反射的UI测试的更多相关文章
- 《软件测试自动化之道》读书笔记 之 基于Windows的UI测试
<软件测试自动化之道>读书笔记 之 基于Windows的UI测试 2014-09-25 测试自动化程序的任务待测程序测试程序 启动待测程序 获得待测程序主窗体的句柄 获得有名字控件的 ...
- 《软件测试自动化之道》读书笔记 之 底层的Web UI 测试
<软件测试自动化之道>读书笔记 之 底层的Web UI 测试 2014-09-28 测试自动化程序的任务待测程序测试程序 启动IE并连接到这个实例 如何判断待测web程序完全加载到浏览 ...
- 软件测试自动化之- 基于反射的UI自动化测试框架 - UI Automation Test Framework
测试自动化程序的任务 基于反射的ui测试自动化程序,要完成的6项任务: 通过某种方式从测试套件程序中运行待测程序(AUT: Applicaton Under Test),以便于两个程序之间进行通信 操 ...
- 《软件测试自动化之道》读书笔记 之 SQL 存储过程测试
<软件测试自动化之道>读书笔记 之 SQL 存储过程测试 2014-09-28 待测程序测试程序 创建测试用例以及测试结果存储 执行T-SQL脚本 使用BCP工具导入测试用例数据 ...
- 《软件测试自动化之道》读书笔记 之 XML测试
<软件测试自动化之道>读书笔记 之 XML测试 2014-10-07 待测程序测试程序 通过XmlTextReader解析XML 通过XmlDocument解析XML 通过XmlPa ...
- 《Essential C++》读书笔记 之 基于对象编程风格
<Essential C++>读书笔记 之 基于对象编程风格 2014-07-13 4.1 如何实现一个class 4.2 什么是Constructors(构造函数)和Destructor ...
- 【哲学角度看软件测试】要想软件“一想之美”,UI 测试少不了
摘要:软件测试的最高层次需求是:UI测试,也就是这个软件"长得好不好看". 为了让读者更好地理解测试,我们从最基础的概念开始介绍.以一个软件的"轮回"为例,下图 ...
- <<google软件测试之道>>读书笔记
以前一直从开发的角度来看待测试,看完这本书以后感觉错了,难怪之前公司的测试一直搭建不起来 1.开发人员,开发测试人员,测试人员 * 开发人员负责开发 * 开发测试人员近距离接触代码,负责编写测试用例, ...
- 图论——读书笔记(基于BFS广度优先算法的广度优先树)
广度优先树 对于一个图G=(V,E)在跑过BFS算法的过程中会创建一棵广度优先树. 形式化一点的表示该广度 优先树的形成过程是这样的: 对于图G=(V,E)是有向图或是无向图, 和图中的源结点s, 我 ...
随机推荐
- C#并行编程(2):.NET线程池
线程 Thread 在总结线程池之前,先来看一下.NET线程. .NET线程与操作系统(Windows)线程有什么区别? .NET利用Windows的线程处理功能.在C#程序编写中,我们首先会新建一个 ...
- 无状态shiro认证组件(禁用默认session)
准备内容 简单的shiro无状态认证 无状态认证拦截器 import com.hjzgg.stateless.shiroSimpleWeb.Constants; import com.hjzgg.st ...
- Quartz配置
1. Quartz主要配置 属性名称 是否必选 类型 默认值 说明 org.quartz.scheduler.instanceName 否 String QuartzScheduler Schedul ...
- MySql:Table 'database.TABLE_ONE' doesn't exist
1. 问题描述 由于最近使用本地的MySQL奔溃了,在修改管理员密码时,不慎修改错误,导致无法重新写会,甚至按照MySQL官网手册都无法修改正确,遂放弃修改root密码,直接将一个未有数据的纯净版My ...
- OpenStack-Zun 使用
Zun组件简介 Zun是Openstack中提供容器管理服务的组件,于2016年6月建立.Zun的目标是提供统一的Openstack API用于启动和管理容器,支持多种容器技术.Zun原来称为Higg ...
- android monitor 汉化 ddms
作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq.com E-mail: 313134555 @qq.com android.jar\com\androi ...
- Codeforces 989E A Trance of Nightfall 矩阵快速幂+DP
题意:二维平面上右一点集$S$,共$n$个元素,开始位于平面上任意点$P$,$P$不一定属于$S$,每次操作为选一条至少包含$S$中两个元素和当前位置$P$的直线,每条直线选取概率相同,同一直线上每个 ...
- js获取本机id
var hostname = location.hostname; window.location.href="http://"+hostname+":8080/zhib ...
- SQLite日期时间函数
SQLite日期时间函数 SQLite支持以下五个日期时间函数: date(timestring, modifier, modifier, …) time(timestring, modifier, ...
- ArcGIS教程:曲率
摘要 计算栅格表面的曲率,包括剖面曲率和平面曲率. 用法 · 主要输出结果为每个像元的表面曲率,该值通过将该像元与八个相邻像元拟合而得.曲率是表面的二阶导数,或者可称之为坡度的坡度.可供选择的输出曲率 ...