net自动化测试之道基于反射的UI自动化测试—运行被测应用程序

来源:互联网 发布:数控激光切割机编程 编辑:程序博客网 时间:2024/05/16 17:59

Launching an Application Under Test

运行被测应用程序

Problem

You want to launch the AUT so that you canmanipulate it.

问题

如何运行AUT,让我们操作AUT?

Design

Spin off a separate thread of executionfrom the test harness by creating a Thread object and

then associate that thread with anapplication state wrapper class.

设计

在测试套件中通过创建一个单独的线程,并且让封装了应用程序状态的类与该线程关联。

解决方案

using System;

using System.Reflection;

using System.Windows.Forms;

using System.Threading;

class Class1

{

[STAThread]

static void Main(string[]args)

{

try

{

Console.WriteLine("LaunchingForm");

Form theForm=null;

string formName="AUT.Form1";

stringpath="..\\..\\..\\AUT\\bin\\Debug\\AUT.exe";

Assembly a=Assembly.LoadFrom(path);

Type t1=a.GetType(formName);

theForm=(Form)a.CreateInstance(t1.FullName);

AppState aps=new AppState(theForm);

ThreadStart ts=new ThreadStart(aps.RunApp);

Thread thread=new Thread(ts);

thread.ApartmentState=ApartmentState.STA;

thread.IsBackground=true;

thread.Start();

Console.WriteLine("\nFormlaunched");

}

catch(Exception ex)

{

Console.WriteLine("Fatalerror:"+ex.Messag

}

}//Main()

private class AppState

{

public readonly Form formToRun;

public AppState(Form f)

{

this.formToRun=f;

}

public void RunApp()

{

Application.Run(formToRun);

}

}//class AppState

//class Class1

To test a Windows-based form applicationthrough its UI using reflection techniques,

you must launch the application on aseparate thread of execution within the test-harness

process.If,instead,you launch an AUT usingthe Process.State()method like this:

stringexePath="..\\..\\..\\AUT\\bin\\Debug\\AUT.exe";

System.Diagnostics.Process.Start(exePath);

the application will launch,but your testharness will not be able to directly communicate

with the application because the harnessand the application will be running in separate

processes.The trick to enableharness-application communication is to spin off a separate

thread from the harness.This way,theharness and the application will be running in the same

process context and can communicate witheach other.

使用反射通过UI测试winform应用程序,必须在测试套件进程中创建单独的线程让应用程序运行。如果不这样,我们使用Process.Start()方法运行AUT:

stringexePath="..\\..\\..\\AUT\\bin\\Debug\\AUT.exe";

System.Diagnostics.Process.Start(exePath);

应用程序可以启动,但是测试套件却不能直接与该应用程序通信,因为二者运行在不同的进程中。让测试套件和AUT通信的技巧是在测试套件进程中开一个单独的线程运行AUT。这样测试套件就和AUT运行在相同的进程上下文中,彼此直接可以通信。

Comments

If your test harness is a consoleapplication,you can add the following using statements so

you won’t have to fully qualify classes andobjects:

using System.Reflection;

using System.Windows.Forms;

using System.Threading;

The System.Reflection namespace houses theprimary classes you’ll be using to access

the AUT.The System.Windows.Forms namespaceis not accessible to a console application

by default,so you must add a projectreference to the System.Windows.Forms.dll file.The

System.Threading namespace allows you to create a separate thread of execution forthe AUT.

注解

如果我们的测试套件是一个控制台应用程序,添加下列using语句编写程序的时候就可以直接使用这些命名空间下的类:

using System.Reflection;

using System.Windows.Forms;

using System.Threading;

System.Reflection命名空间中包含了我们访谈AUT的主要的类。System.Windows.Forms的引用在控制台应用程序中默认不会添加,我们必须自己添加该引用。System.Threading命名空间可以让我们为AUT创建单独执行线程。

Start by getting a reference to theapplication Form object:

Form theForm=null;

stringformName="AUT.Form1";

stringpath="..\\..\\..\\AUT\\bin\\Debug\\AUT.exe";

Assemblya=Assembly.LoadFrom(path);

Typet1=a.GetType(formName);

theForm=(Form)a.CreateInstance(t1.FullName);

The heart of obtaining a reference to theForm object under test is to use the Assembly.

CreateInstance()method.This is slightly tricky because CreateInstance()iscalled from the

context of an Assembly object and acceptsan argument for the full name of the instance being

created.Furthermore,an Assembly object iscreated using a factory mechanism instead of the

more usual constructor instantiation withthe new keyword.Additionally,the full name argu-

ment is called from a Type context.Inshort,you must first create an Assembly object using

Assembly.Load(),passing in the path to theassembly.Then you create a Type object using

Assembly.GetType(),passing in the full Form class name.And,finally,you create areference to

the Form object under test using Assembly.CreateInstance(),passingin the Type.FullName

property.Notice that you must use the fullform name(e.g.,"AUT.Form1")rather than the

shortened formname(e.g.,"Form1").

首先获取应用程序Form对象:

Form theForm=null;

string formName="AUT.Form1";

stringpath="..\\..\\..\\AUT\\bin\\Debug\\AUT.exe";

Assembly a=Assembly.LoadFrom(path);

Type t1=a.GetType(formName);

theForm=(Form)a.CreateInstance(t1.FullName);

获取被测Form对象引用的关键是使用Assembly.CreateInstance()方法。在Assembly对象上调用CreateInstance()方法,并且给它传递需要创建的实例的完成名称,这种创建方式可能有点难理解。而且,Assembly对象不是通过new出来的,它是用工厂模式创建出来的。另外,参数的完整名称是通过Type对象调用的。简言之,首先必须用Assembly.Load(),给它传递完整的Form路径,创建Assembly对象,然后使用Assembly.GetType()创建Type对象,给它传递完成的Form类的名称,最后,使用Assembly.CreateInstance()创建被测Form对象的引用,给它传递Type.FullName属性。注意,必须使用完成的Form名称(如:AUT.Form1),而不是被简化的(如:Form1)。

The code to launch the Form under test isbest understood by working backwards.The

goal is to create a new Thread object andthen call its Start()method;however,to create

a Thread object,you need to pass aThreadStart object to the Thread constructor.To create a

ThreadStart object,you need to pass atarget method to the ThreadStart constructor.This tar-

get method must return void,and it is themethod to invoke when the thread begins execution.

Now in the case of a Form object,you wantto call the Application.Run()method.Although it

seems a bit awkward,the easiest way to passApplication.Run()to ThreadStart is to create a

separate wrapper class:

private class AppState

{

public readonly Form formToRun;

public AppState(Form f)

{

this.formToRun=f;

}

public void RunApp()

{

Application.Run(formToRun);

}

}

This AppState class is just a wrapperaround a Form object and a call to the Application.Run()

method.We do this to pass Application.Run()to ThreadStart in a convenientway.With this class

in place,you can instantiate an AppStateobject and pass Application.Run()indirectly to the

ThreadStart constructor:

AppState aps=new AppState(theForm);

ThreadStart ts=new ThreadStart(aps.RunApp);

With the ThreadStart object created,you cancreate a new Thread,set its properties if nec-

essary,and start the thread up:

Thread thread=new Thread(ts);

thread.ApartmentState=ApartmentState.STA;

thread.IsBackground=true;

thread.Start();

An alternative to creating a Thread objectdirectly is to call the ThreadPool.QueueUserWorkItem()

method.That method creates a threadindirectly and requires a starting method to be passed to a

WaitCallBack object.This approach would look like

Form theForm=null;

stringformName="AUT.Form1";

stringpath="..\\..\\..\\AUT\\bin\\Debug\\AUT.exe";

Assemblya=Assembly.LoadFrom(path);

Type t1=a.GetType(formName);

theForm=(Form)a.CreateInstance(t1.FullName);

ThreadPool.QueueUserWorkItem(newWaitCallback(RunApp),theForm);

where

static voidRunApp(object o)

{

Application.Run(oas Form);

}

This ThreadPool technique is somewhatsimpler than the ThreadStart solution but does not

give you as much control over the thread of execution.

You can increase the modularity of thistechnique by refactoring your code as a method:

static Form LaunchApp(stringpath,string formName)

{

Form result=null;

Assemblya=Assembly.LoadFrom(path);

Type t=a.GetType(formName);

result=(Form)a.CreateInstance(t.FullName);

AppState aps=newAppState(result);

ThreadStart ts=newThreadStart(aps.RunApp);

Thread thread=new Thread(ts);

thread.Start();

return result;

}

which you can call like this:

Form theForm=null;

stringpath="..\\..\\..\\AUT\\bin\\Debug\\AUT.exe";

stringformName="AUT.Form1";

theForm=LaunchApp(path,formName);

下面运行被测Form的代码很好理解了。目标是创建一个新的Thread对象,然后调用它的Start()方法。但是要创建Thread对象,我们需要给Thread构造函数传递一个ThreadStart对象。要创建ThreadStart对象呢,需要给ThreadStart的构造函数传递一个目标方法。这个目标方法返回值必须是void的,在线程开始执行的时候,该方法被唤醒。现在,就Form对象来说,我们需要调用的是Application.Run()方法。为了将Application.Run()传给ThreadStart,我们创建一个单独的封装类,虽然这看起来有点笨拙,但是这是给ThreadStart传递返回void的目标方法最简单的方式:

private class AppState

{

public readonly Form formToRun;

public AppState(Form f)

{

this.formToRun=f;

}

public void RunApp()

{

Application.Run(formToRun);

}

}

这个AppState类仅仅是对Form对象的封装,并且只有Appliction.Run()方法的调用。我们这样做是为了方便把Application.Run()传给ThreadStart。有了这个类,我们可以创建一个AppState的对象,将Application.Run()间接传给ThreadStart构造函数:

AppState aps=new AppState(theForm);

ThreadStart ts=new ThreadStart(aps.RunApp);

有了ThreadStart对象,我们可以创建新线程了,并且启动它,如果有必要可以设置它的属性:

Thread thread=new Thread(ts);

thread.ApartmentState=ApartmentState.STA;

thread.IsBackground=true;

thread.Start();

另外可以通过调用ThreadPool.QueueUserWorkItem()方法直接创建一个Thread对象。上面那种方式是间接创建,并且需要一个启动方法传给WaitCallBack对象。使用这种方式代码如下:

Form theForm=null;

string formName="AUT.Form1";

stringpath="..\\..\\..\\AUT\\bin\\Debug\\AUT.exe";

Assembly a=Assembly.LoadFrom(path);

Type t1=a.GetType(formName);

theForm=(Form)a.CreateInstance(t1.FullName);

ThreadPool.QueueUserWorkItem(newWaitCallback(RunApp),theForm);

在程序的某处创建一个static的RunApp方法:

static void RunApp(object o)

{

Application.Run(o as Form);

}

使用ThreadPool从某种角度来讲要比使用ThreadStart简单,但是对于线程执行控制不如ThreadStart灵活。

我们可以通过重构代码增加模块性:

static Form LaunchApp(string path,stringformName)

{

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;

}

然后这样调用:

Form theForm=null;

stringpath="..\\..\\..\\AUT\\bin\\Debug\\AUT.exe";

string formName="AUT.Form1";

theForm=LaunchApp(path,formName);

原创粉丝点击