基本思想:
操纵应用程序的窗体,从而模拟用户对窗体所实施的moving, resizing操作。
主要应用到了Drawing类库,与反射,委托的知识。
核心是应用PropertyInfo.SetValue()方法和Form.Invoke(delegate, argments)方法。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; using System.Reflection; using System.Drawing; namespace TestingAUT { class Program { static void Main(string[] args) { try { Console.WriteLine("Launching Form...."); //Form theForm = null; string formName = "AUT.Form1"; string Path = @"C:/Users/btslabs/Desktop/TE/AUT/AUT/bin/Debug/AUT.exe"; Form theForm = LaunchApp(Path, formName); MoveForm(theForm, 10, 20); ReSizeForm(theForm, 300, 300); Console.WriteLine("AUT launched!"); Thread.Sleep(1500); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine("Fatal error: " + ex.Message); Console.ReadKey(); } } //模块1,运行待测程序. static Form LaunchApp(string path, string formName) { Form result = null; //首先获得一个待测程序窗体对象的引用 //通过工厂机制来创建Assembly对象。 Assembly a = Assembly.LoadFrom(path); //Type的使用,其实就是不知道类名以及类的成员信息的情况下,一种泛泛使用class的概念. Type t = a.GetType(formName); //createInstance()是Assembly对象的方法, 它的参数是被创建实例的全名. //全名称参数是通过一个Type对象来调用的. result = (Form)a.CreateInstance(t.FullName); //using the wrapper class and Thread to launch the App AppState aps = new AppState(result); //要创建一个ThreadStart对象,需要传一个目标方法(Target method)给ThreadStart的构造函数. //这个目标方法必须返回void类型 //当线程开始执行时,要调用的就是这个目标方法. ThreadStart ts = new ThreadStart(aps.RunApp); //要创建一个Thread对象,需要传一个ThreadStart对象给Thread的构造函数 Thread thread = new Thread(ts); thread.ApartmentState = ApartmentState.STA; thread.IsBackground = true; thread.Start(); return result; } //模块2,设置窗体属性,以达到移动窗体目的。 static void MoveForm(Form f, int x, int y) { Console.WriteLine("/nSetting Form1 Location to (x, y)"); System.Drawing.Point pt = new System.Drawing.Point(x, y); //创建一个对象数组,保存需要传给delegate的参数,然后这些参数会传给与delegate相关联的实际方法 object[] o = new object[] { f, "Location", pt }; SetFormProperty sfp1 = new SetFormProperty(); Delegate d = new SetFormPropertyValueHandler(sfp1.SetFormPropertyValue); if (f.InvokeRequired) { /*调用PropertyInfo.SetValue()方法时,并不是从窗体的主线程里,而是在由自动化测试程序所创建的一个线程里调用SetValue(), * 在这种情况下,不能直接调用SetValue(),而是以Form.Invoke()方法间接调用SetValue()。 从而确认测试代码与待测程序在一个进程中能通过线程相互通信。*/ f.Invoke(d, o); } else { Console.WriteLine("Unexpected logic flow"); } } //模块三,改变窗体大小 static void ReSizeForm(Form f, int Width, int Height) { Console.WriteLine("/nSetting Form1 Size to {0}*{1}",Width,Height); System.Drawing.Size size = new System.Drawing.Size(Width, Height); //创建一个对象数组,保存需要传给delegate的参数,然后这些参数会传给与delegate相关联的实际方法 object[] o = new object[] { f, "Size", size }; SetFormProperty sfp1 = new SetFormProperty(); Delegate d = new SetFormPropertyValueHandler(sfp1.SetFormPropertyValue); if (f.InvokeRequired) { /*调用PropertyInfo.SetValue()方法时,并不是从窗体的主线程里,而是在由自动化测试程序所创建的一个线程里调用SetValue(), * 在这种情况下,不能直接调用SetValue(),而是以Form.Invoke()方法间接调用SetValue()。 从而确认测试代码与待测程序在一个进程中能通过线程相互通信。*/ //对象数组o中的参数是供delegate委托d来使用的. f.Invoke(d, o); } else { Console.WriteLine("Unexpected logic flow"); } } } //定义一个外覆类以使Application.Run()传给ThreadStart class AppState { public Form myForm; public AppState(Form F) { this.myForm = F; } //目标是将一个有参数的方法转换成一个无参数的,且返回值为void的方法,供ThreadStart构造函数使用。 //因为ThreadStart构造函数使用一个委托,参数必须只是一个是无参数的方法名. public void RunApp() { Application.Run(myForm); } } delegate void SetFormPropertyValueHandler(Form f, string propertyName,object newValue); //创建一个“设置窗体属性类”,在其中定义一个实例方法供委托使用。 class SetFormProperty { //必须是实例方法,若是静态方法,在委托中使用不便 // public void SetFormPropertyValue(Form f, string propertyName, object newValue) { //而PropertyInfo对象需要由一个Type对象调用 Type t = f.GetType(); //SetValue()方法又需要由一个PropertyInfo对象来调用, PropertyInfo pi = t.GetProperty(propertyName); //.NET中有一个PropertyInfo.SetValue()方法来设置窗体的属性 pi.SetValue(f, newValue, null); } } }