Spiga

Windows phone 应用开发[10]-自动化测试

2012-01-10 15:51 by chenkai, 1404 visits, 收藏, 编辑

本篇承接上篇Windows phone 应用开发[9]-单元测试,当在Unit TEst 确定了测试用例的方案.在单个模块单元测试通过开始走模块之间集成时.其实很多测试工作就可以开发人员手中开始向测试人员转移.类似针对单个模块,测试团队可以根据已经确定测试用例批量执行.得到实际结果与期望结果进行比较.而这个过程测试执行者无需了解代码如何执行.也就是俗称黑盒测试.

而对于单个模块的回归测试而言.为了提高效率引入自动化测试的概念.可惜的是目前并没有任何可用于Windows phone Application应用程序的自动化测试工具.曾在Silverlight中使用过自动化测试工具-Ranorex,花费点时间尝试通过Ranorex在WP 上执行自动化测试.发现存在很多应用程序与测试工具之间很多不可控的问题.

那么对于单个模块回归测试而言.需要哪些过程可以自动化?分析如下:

自动化流程:

[1]:模拟器控制-启动或关闭Windows phone 模拟器.

[2]: 自动在模拟器上安装并执行测试用例应用程序.

[3]: 收集测试结果

[4]: 卸载测试用应用程序前恢复模拟器

首先来看看应用程序安装执行过程.目前只能通过Application Development Tool部署真机上:

2012-01-10_112505

now,如果我们把这个过程自动化处理.问题就出现了如何在不使用Application Development Tool情况把XAP安装包安装到模拟器或真机上? 同样针对模拟器控制.如何能够在代码自动控制并运行XAP安装包?

针对这个问题.Windows phone 类库中提供一个Microsoft.SmartDevice.Connectivity.DLL用来实现与模拟器或设备进行交互.现在针对原有解决方案添加一个命名为DeveiceAutomationTest_Desktop的Console类型应用程序用来做回归测试自动化的控制台.解决方案结构如下:

2012-01-10_113843

要实现对模拟器交互控制 需要在控制台应用程序添加对Microsoft.SmartDevice.Connectivity.DLL引用,引用地址C:\Program Files (x86)\Common Files\microsoft shared\Phone Tools\CoreCon\10.0\Bin:

2012-01-09_143729

添加引用后,实现对应设备控制的操作.连接并安装初始化XAP到模拟器中,添加一个设备控制类DeviceManager类,首先要根绝设备类型模拟器或真机获取设备的控制设备的Device对象:

   1:          /// <summary>
   2:          /// Get Current Support Platform Device Object
   3:          /// </summary>
   4:          /// <param name="localId">Region Local Id</param>
   5:          /// <returns>Device Object</returns>
   6:          public static Device GetSupportPlatformDeviceObj(int localId,bool isDevice)
   7:          {
   8:              //Note By chenkai:
   9:              //1033 is the LCID for English, United States and 1031 is the LCID for German, Germany.
  10:              //Please Check List of Locale ID (LCID) Values as Assigned by Microsoft.
  11:              //http://msdn.microsoft.com/zh-cn/goglobal/bb964664.aspx
  12:              //Now Use English is 1031
  13:   
  14:              if (localId == 0)
  15:                  localId = 1031;
  16:              Device currentDeviceObj = null;
  17:              DatastoreManager datastoreManagerObj = new DatastoreManager(localId);
  18:              Platform wp7Platform = datastoreManagerObj.GetPlatforms().Single(p => p.Name == "Windows Phone 7");
  19:              if (wp7Platform != null)
  20:              {
  21:                  if (isDevice)
  22:                      currentDeviceObj = wp7Platform.GetDevices().Single(x => x.Name == "Windows Phone Device");
  23:                  else
  24:                      currentDeviceObj = wp7Platform.GetDevices().Single(x => x.Name == "Windows Phone Emulator");
  25:              }
  26:              return currentDeviceObj;
  27:          }

localid是在初始化静态 DatastoreManager 对象时是一个区域设置标识符 (LCID),这是一个标识语言、国家和/或地区的整数. 当然这里采用1031德国地区.针对这个区域Localid的设置请参考官方给出的List of Locale ID (LCID) Values as Assigned by Microsoft 列表.

获取数据控制Device对象后.可以连接设备并把XAP包安装并初始化到设备中:

   1:          /// <summary>
   2:          /// Connection Emulator Device
   3:          /// </summary>
   4:          /// <param name="deviceObj">Device Object</param>
   5:          /// <param name="applicationGuid">Application Guid</param>
   6:          /// <param name="iconFilePath">IconFile Path</param>
   7:          /// <param name="xapFileName">XapFile Name</param>
   8:          /// <returns>return is Connection Device</returns>
   9:          public static void ConnectionDeviceInstallApp(Device deviceObj,Guid applicationGuid,string iconFilePath,string xapFileName)
  10:          {
  11:              if (!string.IsNullOrEmpty(applicationGuid.ToString()))
  12:              {
  13:                  try
  14:                  {
  15:                      deviceObj.Connect();
  16:                      ApplicationManager.InstallApplicationAndLaunch(deviceObj, applicationGuid, iconFilePath, xapFileName);
  17:                  }
  18:                  finally
  19:                  {
  20:                      deviceObj.Disconnect();
  21:                  }
  22:              }
  23:          }

这个方法中需要提供Device设备操作对象.以及XAP安装包和对应应用程序图标的文件路径. 其实这里并不一定非得采用控制台的方式.也可以做一个Winform以可见UI的方式.至于Guid则是测试项目WP7AutomationTest_Demo.Test-WMAppmainfest.xml文件中的ProductId相对应:

   1:    <App xmlns="" ProductID="{b81d87a6-3afd-4f21-9a60-3c5ae0921fd2}" Title="WP7AutomationTest_Demo.Test"/>

该方法中执行了两个操作.链接设备.安装XAP包到设备中.安装XAP操作通过ApplicationManager类中InstallApplicationAndLaunch方法 .定义如下:

   1:          public static void InstallApplicationAndLaunch(Device deviceObj, Guid applicationGuid, string iconFileName, string xapFileName)
   2:          {
   3:              if (!string.IsNullOrEmpty(applicationGuid.ToString()))
   4:              {
   5:                  //Install and Luanch
   6:                  UnstallApplicationAndClear(deviceObj, applicationGuid);
   7:                  deviceObj.InstallApplication(applicationGuid, applicationGuid, "AutomationAPP", iconFileName, xapFileName);
   8:                  RemoteApplication currentApplication = deviceObj.GetApplication(applicationGuid);
   9:                  if (currentApplication != null)
  10:                      currentApplication.Launch();
  11:              }
  12:          }

通过Device对象初始化XAP 并Lauch,.在执行安装XAP之前 如果正在运行则立即中断.如果已经安装该应用程序则需删除卸载:

   1:          public static void UnstallApplicationAndClear(Device deviceObj, Guid applicationGuid)
   2:          {
   3:              if(!string.IsNullOrEmpty(applicationGuid.ToString()))
   4:              {
   5:                  if (deviceObj.IsApplicationInstalled(applicationGuid))
   6:                  {
   7:                      RemoteApplication remoteApplication = deviceObj.GetApplication(applicationGuid);
   8:                      if (remoteApplication != null)
   9:                      {
  10:                          //Remove Application
  11:                          remoteApplication.TerminateRunningInstances();
  12:                          remoteApplication.Uninstall();
  13:                      }
  14:                  }
  15:              }
  16:          }

well.如此封装号对设备的初始化的控制.开始执行ProgramMain 方法:

   1:          static void Main(string[] args)
   2:          {
   3:              //Base ApplicationInfo
   4:              string xapfile = @"..\..\..\WP7AutomationTest_Demo.Test\Bin\Debug\WP7AutomationTest_Demo.Test.xap";
   5:              string iconfile = @"..\..\..\WP7AutomationTest_Demo.Test\Bin\Debug\WApplicationIcon.png";
   6:              ApplicationBaseInfo currentBaseInfo = new ApplicationBaseInfo(xapfile, iconfile, new Guid("b81d87a6-3afd-4f21-9a60-3c5ae0921fd2"));
   7:   
   8:              //DeviceManager Object
   9:              Device deviceObj = DeviceManager.GetSupportPlatformDeviceObj(1031,false);
  10:              using (ServiceHost currentHost = new ServiceHost(typeof(TestResultReportService)))
  11:              {
  12:                  currentHost.Open();
  13:                  Console.WriteLine("Current Service is Open...");
  14:                  Console.WriteLine("Connecting to Windows phone Emluator Device...");
  15:   
  16:                  try
  17:                  {
  18:                      //Device Connection
  19:                      deviceObj.Connect();
  20:                      Console.WriteLine("Windows phone Emluator Device Connected...");
  21:                      ApplicationManager.InstallApplicationAndLaunch(deviceObj, currentBaseInfo.ApplicationGuid, currentBaseInfo.IconFilePath, currentBaseInfo.XapFilePath);
  22:                      Console.WriteLine("Wait For Run TestCase Compated...");
  23:   
  24:                      lastendTestRunEvent.WaitOne(12000);
  25:   
  26:                      Console.WriteLine("Test Case is Run Complate...");
  27:                      ApplicationManager.UnstallApplicationAndClear(deviceObj, currentBaseInfo.ApplicationGuid);
  28:                      Console.WriteLine("Application is UnStall and Clear...");                   
  29:                  }
  30:                  finally
  31:                  {
  32:                      deviceObj.Disconnect();
  33:                  }
  34:   
  35:                  //currentHost.Close();
  36:              }

Main方法执行的流程.首先部署XAP停止正在应用程序.并从设备中移除.然后连接设备 部署XAP包. 执行TEstCase测试用例.获取测试用例反馈结果.卸载当前应用程序并清空数据状态.回复测试运行前.测试效果:

2012-01-09_183143

正常运行时如果模拟器没有运行会自动打开模拟器 安装XAP包运行测试用例.当控制台获取测试结果后.卸载当前当前测试用例.一个完整回归测试流程就建立成功了.而发现可以把整个执行流程全部自动化代码控制.当然可以把回归测试在执行多条数据时多次自动化执行.整个执行流程控制台操作如下:

2012-01-10_150414

在main方法同时建立一个WCF服务.该服务的主要目的是在单元测试用例测试完成后通知控制台停止调用. 另外一个目的是把SUTF框架的测试结果传递给控制台.存在一个测试结果文件中保存在本地并输出到控制台中:

   1:      public class TestResultReportService : ITestResultReportService
   2:      {
   3:          public void SaveTestResultFile(string filename, string filecontent)
   4:          {
   5:              var outputfile = Path.Combine(@"..\..\..\DeveiceAutomationTest_Desktop\Bin\Debug\", filename);
   6:              if (File.Exists(outputfile))
   7:              {
   8:                  File.Delete(outputfile);
   9:              }
  10:              File.WriteAllText(outputfile, filecontent);
  11:              Console.WriteLine(filecontent);
  12:          }
  13:          
  14:          public void GetFinalTestResult(bool failure, int failures, int totalScenarios, string message)
  15:          {
  16:              Console.WriteLine(message);
  17:              Program.EndTestRun();
  18:          }
  19:      }

而针对测试用例应用程序WP7AutomationTest_Demo.Test需要额外引用该服务.重新建立一个TestBaseReportService类用来重写TestReportingProvider实体类中方法实现.测试结果通过服务以消息的形式传递给控制台.保存在文件中: 首先添加引用:

   1:  using Microsoft.Silverlight.Testing;
   2:  using Microsoft.Silverlight.Testing.Harness;
   3:  using Microsoft.Silverlight.Testing.Service;
   4:  using Microsoft.VisualStudio.TestTools.UnitTesting;

重写方法:

   1:      public class TestBaseReportService:TestReportingProvider
   2:      {
   3:          //ServiceClient
   4:          UnitTestService.TestResultReportServiceClient reportClient = new UnitTestService.TestResultReportServiceClient();
   5:          public TestBaseReportService(TestServiceProvider serviceProvider) : base(serviceProvider) { }
   6:   
   7:          public override void ReportFinalResult(Action<ServiceResult> callback, bool failure, int failures, int totalScenarios, string message)
   8:          {
   9:              this.IncrementBusyServiceCounter();
  10:              reportClient.SaveTestResultFileCompleted += (se, x) =>
  11:              {
  12:                  this.DecrementBusyServiceCounter();
  13:              };
  14:              reportClient.GetFinalTestResultAsync(failure, failures, totalScenarios, message);
  15:              base.ReportFinalResult(callback, failure, failures, totalScenarios, message);
  16:          }
  17:   
  18:          public override void WriteLog(Action<ServiceResult> callback, string logName, string content)
  19:          {
  20:              this.IncrementBusyServiceCounter();
  21:              reportClient.SaveTestResultFileCompleted += (s, e) =>
  22:              {
  23:                  this.DecrementBusyServiceCounter();
  24:              };
  25:              reportClient.SaveTestResultFileAsync(logName, content);
  26:              base.WriteLog(callback, logName, content);
  27:          }
  28:      }

ok。定义重写服务之后还需要Silverlight Unit TESt FrameWork框架进行测试结果的关联.这很关键.SUTF框架将通过该服务来提交报告. 在mainpage定义输出的页面添加如下Code:

   1:             var reportSetting = UnitTestSystem.CreateDefaultSettings();
   2:              if (reportSetting!=null)
   3:              {
   4:                  reportSetting.TestService.RegisterService(TestServiceFeature.TestReporting, new TestBaseReportService(reportSetting.TestService));
   5:              }

一整个回归测试自动化流程建立完毕",编译通过执行:

2012-01-10_153241

well.可以看到把整个回归测试整个流程完全自动化用Code处理了. 而控制台应用就类似自动化时可以复用处理的脚本一样.可以复用.依然能够看到控制台中存在几个需要及时制定的变量类似.XAP 包路径等.也可以封装可见的UI指定.当我们修改审核玩测试用例后.就可以通过该控制台应用程序.定期执行自动化回归的测试.生成测试报告.很有效保证代码集成时质量.本篇只是提供自动化解决一种方案.

源码如下:

 /Files/chenkai/WP7AutomationTest_Demo.rar

Add your comment

15 条回复

  1. #1楼 AndyYu      2012-01-10 16:19
    很不错哦,学习了
     回复 引用 查看   
  2. #2楼[楼主] chenkai      2012-01-10 16:25
    @AndyYu
    :)
     回复 引用 查看   
  3. #3楼 施炯      2012-01-11 08:06
    学习了。
     回复 引用 查看   
  4. #4楼[楼主] chenkai      2012-01-11 10:42
    @施炯
    :)
     回复 引用 查看   
  5. #5楼 _雪松_      2012-01-11 13:07
    认真学习。请把此博文作为MSDN论坛中该问题的解答吧。
     回复 引用 查看   
  6. #6楼[楼主] chenkai      2012-01-11 13:50
    @_雪松_
    已经给出答案了.
     回复 引用 查看   
  7. #7楼 娄底房网[未注册用户]2012-01-12 15:22
    总算找到了这个正确答案,博主帮我解决了一个问题,感谢。。
     回复 引用   
  8. #8楼 thankchunzi      2012-01-13 23:04
    您好,Windows Phone 7 上如何批量的编译源代码为XAP包?
    比如我的客户端是给淘宝卖家做的,那每一个店铺的Url都是不一样的(其实只有店铺的ID不一样),我现在有一个List,存储的都是店铺的ID。如何批量的生成多个客户端呢?而且某些图片的路径也是要改改,不然店铺都长的一样了。请您指点
     回复 引用 查看   
  9. #9楼 thankchunzi      2012-01-13 23:05
    不好意思,点错地方了。
    您好,Windows Phone 7 上如何批量的编译源代码为XAP包?
    比如我的客户端是给淘宝卖家做的,那每一个店铺的Url都是不一样的(其实只有店铺的ID不一样),我现在有一个List,存储的都是店铺的ID。如何批量的生成多个客户端呢?而且某些图片的路径也是要改改,不然店铺都长的一样了。请您指点
     回复 引用 查看   
  10. #10楼 llmm1013      2012-01-30 11:49
    请问博主,我连接模拟器没问题,但是连接真机的时候在调用launch时报错,不能启动应用,什么原因啊?博主能帮忙解答下吗?
    Microsoft.SmartDevice.Connectivity.SmartDeviceException was unhandled
    Message=0x80040202
    Source=Microsoft.SmartDevice.Connectivity
    ErrorCode=-2147220990
    StackTrace:
    at Microsoft.SmartDevice.Connectivity.RemoteApplication.Launch()
    at DeveiceAutomationTest_Desktop.DeveiceControl.ApplicationManager.InstallApplicationAndLaunch(Device deviceObj, Guid applicationGuid, String iconFileName, String xapFileName) in E:\work\WP7AutomationTest_Demo\DeveiceAutomationTest_Desktop\DeveiceControl\ApplicationManager.cs:line 29
    at DeveiceAutomationTest_Desktop.Program.Main(String[] args) in E:\work\WP7AutomationTest_Demo\DeveiceAutomationTest_Desktop\Program.cs:line 38
    at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
    at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
    at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
    at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
    at System.Threading.ThreadHelper.ThreadStart()
    InnerException:
     回复 引用 查看   
  11. #11楼[楼主] chenkai      2012-01-30 13:07
    @llmm1013
    关于这个问题.原来执行真机测试时.注意链接的这段代码:
     if (isDevice)
      currentDeviceObj = wp7Platform.GetDevices().Single(x => x.Name == "Windows Phone Device");
     else
      currentDeviceObj = wp7Platform.GetDevices().Single(x => x.Name == "Windows Phone Emulator");
    

    在参数wp7Platform.GetDevices()这个集合中可能真机对应的Name. 会发生变化. 你可以先加上断点.真机执行时看看里面值是否和Windows Phone Device对应.?如果不对应则需要修改一致即可.如有其它问题请评论提出,
     回复 引用 查看   
  12. #12楼 llmm1013      2012-01-30 15:11
    @chenkai
    wp7Platform.GetDevices()中的Name属性对应是Windows Phone Device没错,继续debug到deviceObj.Connect();可以连接设备,但是到ApplicationManager.InstallApplicationAndLaunch中的currentApplication.Launch();就报上面的错了。
     回复 引用 查看   
  13. #13楼[楼主] chenkai      2012-01-30 16:32
    @llmm1013
    这个目前我还没看出是什么原因.原来在测试真机时时没有任何问题的. 这个可能访问发布权限的问题.只是猜测.问题原因应该出现卸载Xap完成Launch这个步骤上.
    暂无解决方案. 我先看看.
     回复 引用 查看   
  14. #14楼 llmm1013      2012-01-31 11:16
    @chenkai
    多谢楼主,Demo能安装到手机中,就是luanch时报那个错。
     回复 引用 查看   
  15. #15楼 adaaa[未注册用户]2012-02-22 06:02
    请问Microsoft.SmartDevice.Connectivity.DLL引入报错,说没有用window phone runtime 编译,怎么解决?
     回复 引用   
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 2318221 Pj6XP5s2hNY=
无觅相关文章插件,快速提升流量