VisualStudio2010新增了UI测试功能。想必很多人都尝试过了。作为录制/回放工具的一种,我想用它在web应用上也尝试一下。虽然知道web上的自动化测试历来是极其麻烦的一种,但考虑到操作系统是ms家的、浏览器是ms家的、自动化工具是ms家的甚至C#语言也是ms家的,说不定兼容性能好一些。经过试用之后,发现录制的脚本可以在IDE环境中执行(在测试管理器里应该也是可以的,不过仍然没有脱离环境),但还没有达到我进行无人值守测试的“远大目标”。于是查阅资料写了一个简易的执行框架。工作流程是这样的:

将这个框架编译成一个exe文件,并加入计划任务管理器中定时执行,框架完成的工作是根据配置文件依次执行脚本并给出执行结果的日志。

后续又做了一些额外的工作是,自动获取最新版本,自动解压部署等。把这些都放入一个批处理文件中。所以最后在计划任务中执行的是批处理文件。不过那与visualstudio2010无关了。

框架完整代码如下:

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.IO;using System.Reflection;using System.Xml;namespace autotest{    class Program    {        static void Main(string[] args)        {            string logFilePath = @"log\";   //日志写入固定目录"log"            string logFileName;                                string dllPath = @"auto_dll\";        //dll文件取自固定子目录"auto_dll"            string dllName, dllFullName, dllEnterClass, dllRecordClass;            string xmlFileName = @"auto_dll\config.xml";    //取特定配置文件config.xml            int sleepTime;                                  //每个脚本执行完毕后的延时            string dllDescription;                          //从配置文件中读取,用于日志显示            if (!Directory.Exists(logFilePath))            {                Directory.CreateDirectory(logFilePath);     //检查和创建日志文件夹            }                                                        logFileName=logFilePath + "log_" + DateTime.Now.Year.ToString() + "_" + DateTime.Now.Month.ToString() + "_" + DateTime.Now.Day.ToString() + ".log"; ;            StreamWriter sw;            if (!File.Exists(logFileName))            {                sw = File.CreateText(logFileName);      //检查和创建日志文件            }            else            {                sw = File.AppendText(logFileName);            }            sw.WriteLine("[启动时间]:   " + DateTime.Now.ToString());            sw.WriteLine("---------------------------------------------------------------------------------------");            XmlDocument xmlDoc = new XmlDocument();            try            {                xmlDoc.Load(xmlFileName);            }            catch                           //处理xml解析出错异常——配置文件错误直接退出程序            {                sw.WriteLine("xml文件解析错误,系统退出!");                sw.Close();                System.Environment.Exit(0);            }                                XmlNodeList xnl = xmlDoc.GetElementsByTagName("NeedTest");      //查找xml中NeedTest节点            foreach (XmlNode Node in xnl.Item(0).ChildNodes)                //遍历节点下所有子节点,循环            {                XmlNodeList xnl1 = Node.ChildNodes;                dllName = xnl1.Item(0).InnerText;                dllFullName = dllPath + dllName + ".dll";                dllEnterClass = dllName + ".tm";                dllRecordClass = dllName + ".CodedUITest1";                 //取得dll全路径名称、dll中的两个类名称固定采用tm和CodedUITest1                try                {                    sleepTime = Convert.ToInt16(xnl1.Item(1).InnerText);    //转换字符串为延时时间数字值,如有异常默认设置为10;                }                catch                {                    sleepTime = 10;                }                dllDescription = xnl1.Item(2).InnerText;                sw.WriteLine(dllName + ": " + dllDescription);              //取得dll描述信息写入日志                try                {                    Assembly ass1 = Assembly.LoadFrom(dllFullName);         //加载dll                    sw.WriteLine(dllName + ".dll 加载成功");                    Type MyAppType = ass1.GetType(dllEnterClass);                              Type MyAppType1 = ass1.GetType(dllRecordClass);                    object obj = Activator.CreateInstance(MyAppType);       //创建tm对象和CodedUITest1对象                    object obj1 = Activator.CreateInstance(MyAppType1);                    try                    {                        MyAppType.GetMethod("Init").Invoke(obj, null);      //执行tm对象中的Init方法                        MyAppType1.GetMethod("CodedUITestMethod1").Invoke(obj1, null);  //执行CodedUITest1对象中的CodedUITestMethod1方法                    }                    catch(Exception e)                                      //处理执行失败异常,写入日志                    {                        sw.WriteLine(e.Message);                        sw.WriteLine("回放过程失败");                    }                    finally                                                //清理,避免一个脚本执行不成功引起后续脚本都不能执行成功                    {                        try                        {                            MyAppType.GetMethod("Cleanup").Invoke(obj, null);                        }                        catch { throw new System.Exception("Cleanup执行异常"); }                                                    }                }                catch(Exception ee)                                        //捕捉全过程所有异常,写入日志                {                    sw.WriteLine(ee.Message);                    sw.WriteLine(dllName+".dll 运行失败");                    sw.WriteLine();                    System.Threading.Thread.Sleep(new TimeSpan(0, 0, 0, sleepTime, 0));                    continue;                }                sw.WriteLine(dllName+"运行成功");                       //成功,写入日志                sw.WriteLine();                System.Threading.Thread.Sleep(new TimeSpan(0, 0, 0, sleepTime, 0));            }                                                           //下一循环            sw.Close();                         //关闭日志文件        }    }}

之所以考虑这样,是因为开发部门已经实现了持续集成,我也希望能做到持续的回归测试。经过一段时间的实验证明,这个框架本身工作还算稳定,但执行结果嘛——就只能“呵呵”了。具体原因,请等待我在下一篇吐槽。