在目前进行软件测试时,都或多或少的引入了自动化测试的概念,而且市面上也有好多软件自动化方面相关的工具,比如QTP,比如LoadRunner,但是这些工具要么售价不菲,要么对某些方面功能支持的不够全面,那么我们在引入软件自动化测试时,都需要考虑哪些方面呢?当然是最符合自己项目的工具最合适,同时费用也比较低,那么除了市面上这些商业软件外,还有没有哪些方法可以自己动手来做软件的自动化测试呢?答案是肯定的.
本文将介绍实现软件自动化测试其中一种测试方法------反射技术,当然,此种方法仅限于对.Net软件使用,还有一些比较通用的方法,将在后续的文章中进行介绍(包括使用UIAutomation类以及Windows API).
反射技术具体是什么原理,本文将不做任何介绍,大家可以去网上搜索一下,有很多这方面的文章介绍,本文只介绍如何使用反射技术进行.NET软件的UI界面的自动化测试.
废话少说,多做实事!(本文所有代码均在VS2008环境下测试通过)
一. 创建待测试程序
1. 启动VS2008,建立一个C#的WinForm工程,并将工程文件名命名为AUT(Application Under Test)
2. 创建的Form上的按钮布局,如下图所示
3. 一个菜单(包含一个以及菜单File以及一个二级菜单Exit),一个TextBox,一个ComboBox,一个Button以及一个ListBox
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.ComboBox comboBox1;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.ListBox listBox1;
4. 给Button按钮添加一个消息响应函数
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");
}
}
5. 给Exit菜单添加一个消息响应函数
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Application.Exit();
}
6.
给ComboBox控件添加三个Item,分别为paper,rock,scissions
7. 编译待测试程序,生成文件名为AUT.exe的待测试程序
二. 创建自动化测试程序
1. 启动VS2008,创建一个C#控制台程序,并命名为ReflectionUITest
2. 在工程中添加两个引用:System.Windows.Form以及System.Drawing
3. 在工程中添加以下using语句
using System;
using System.Reflection;
using System.Windows.Forms;
using System.Threading;
using System.Drawing;
using System.IO;
4. 编写启动待测试程序的函数
static Form LaunchApp(string path, string formName)
{
Form result = null;
//通过反射技术时,可以通过Assembly类来进行,此处将待测试程序动态加载生成Assembly类
Assembly a = Assembly.LoadFrom(path);
//获取Assembly类的类型
Type t = a.GetType(formName);
//创建Form的实例
result = (Form)a.CreateInstance(t.FullName);
//设置线程启动时运行的函数
AppState aps = new AppState(result);
ThreadStart ts = new ThreadStart(aps.RunApp);
//启动线程
Thread thread = new Thread(ts);
thread.Start();
//返回Form实例
return result;
}
private class AppState
{
public readonly Form formToRun;
public AppState(Form f)
{
this.formToRun = f;
}
public void RunApp()
{
Application.Run(formToRun);
}
}
5. 设置窗体的属性信息
//定义一个委托事件
delegate void SetFormPropertyValueHandler(Form f, string propertyName, object newValue);
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);
are.WaitOne();
}
else
{
//获取窗体的类型
Type t = f.GetType();
//根据指定的属性来获取PropertyInfo信息
PropertyInfo pi = t.GetProperty(propertyName);
//设置属性值
pi.SetValue(f, newValue, null);
are.Set();
}
}
6. 获取窗体的属性
delegate object GetFormPropertyValueHandler(Form f, string propertyName);
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);
are.WaitOne();
return iResult;
}
else
{
Type t = f.GetType();
PropertyInfo pi = t.GetProperty(propertyName);
object gResult = pi.GetValue(f, null);
are.Set();
return gResult;
}
}
7. 设置窗体中指定控件的信息
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信息
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();
}
}
8.
获取窗体中指定控件的信息
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;
}
}
9. 执行指定的函数
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();
}
}
10. 声明需要使用到的变量
static BindingFlags flags = BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Static |
BindingFlags.Instance;
static AutoResetEvent are = new AutoResetEvent(false);
11. 编写测试代码
try
{
Console.WriteLine("\nStart test scenario");
Console.WriteLine("\nLaunching Form1");
Form theform = null;
string formName = "AUT.Form1";
string path = Directory.GetCurrentDirectory() + "\\AUT.exe";
theform = LaunchApp(path, formName);
Thread.Sleep(1000);
Console.WriteLine("\nMoving Form1");
Point pt = new Point(600, 200);
SetFormPropertyValue(theform, "Location", pt);
Console.WriteLine("\nGetting Form1 Location");
Point lt = (Point)GetFormPropertyValue(theform, "Location");
Console.WriteLine("X: " + lt.X.ToString() + " Y: " + lt.Y.ToString());
Console.WriteLine("\nSetting TextBox to 'rock'");
SetControlPropertyValue(theform, "textBox1", "Text", "rock");
Console.WriteLine("\nSetting ComboBox to 'scissors'");
SetControlPropertyValue(theform, "comboBox1", "Text", "scissors");
Console.WriteLine("\nClicking button1");
object[] parms = new object[] { null, EventArgs.Empty };
InvokeMethod(theform, "button1_Click", parms);
bool pass = true;
Console.WriteLine("\nCheckint listBox1 for 'TextBox wins'");
ListBox.ObjectCollection oc = (ListBox.ObjectCollection)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);
InvokeMethod(theform, "exitToolStripMenuItem_Click", parms);
Console.WriteLine("\nEnd test scenario");
}
catch (System.Exception ex)
{
Console.WriteLine("Fatal error: " + ex.Message);
}
12. 编译生成测试程序并运行,OK,大功告成