Connect(); 2016

第 31 卷,第 12 期

Connect(); 移动测试 - 使用 Xamarin Test Cloud 大规模自动测试移动应用

作者:Justin Raczak; 2016

近年来,团队生成和交付软件的方式发生了巨大转变。大家曾认为漫长的需求收集过程可确保产品在首次推出时完美无缺,现在明白了快速学习与快速迭代才是成功的关键要素。随着思维发生转变,工作流也必须随之改变。先是持续数月或数年的开发周期,然后是漫长的瀑布式 QA 各阶段,并不有利于快速学习的实现。必须缩短反馈循环,快速实现细微更改并将其发布给用户。为了方便部署,必须确保软件始终都能正常运行。为此,需要使用测试自动化服务。

借助自动测试,你可以缩短应用测试时间,过去常常需要花费数天或数周的时间。不用等到由数百行新代码组成的冲刺 (sprint) 结束,就可以测试每次提交时添加的细微更改。这种连续测试可以在缺陷一出现就将其暴露出来,并缩短调试缺陷所需的时间。由于应用的行为不断得到验证,因此你可以随时准备向用户部署应用。借助自动测试,你可以在同一天内发现并修复缺陷。不过,移动生态系统中有众多移动设备和操作系统制造商,带来了独特的挑战。

借助 Xamarin Test Cloud,你可以快速轻松地进行大规模自动测试,尽可能少地更改现有工作流。Test Cloud 提供 400 多种独特的设备配置,可便于你验证对用户非常重要的设备型号和操作系统版本所对应的应用行为,免去了构建和管理自有设备实验室所需的费用或管理开销。大多数情况下,你几乎不用更改代码,就可以发掘出巨大价值。

Test Cloud 支持在 C# (UITest)、Ruby (Calabash) 和 Java(Appium 和 Espresso)中创作测试。在本文的项目修改部分中,我将重点介绍我们最需要添加的测试框架(即包含 JUnit 的 Appium),并逐步介绍了需要对项目进行哪些更改才能在 Test Cloud 中运行现有测试。此外,我还将介绍一下 Web 界面。你可以在其中查看测试结果,并对失败的测试进行故障排除。所需的具体修改可能会随时间而改变。有关最新版说明,请访问 bit.ly/2dhp2VQ

在此示例中,我假设需要满足以下先决条件:

  • 已激活的 Test Cloud 帐户(在 bit.ly/2e3YgTy 上注册)
  • 已安装命令行工具(bit.ly/2dcrbXS 上提供了相关说明)
  • 本机 Android 应用程序项目
  • 在 Java 中编写的一系列现有 Appium 测试,其中 JUnit(版本 4.9 及更高版本)符合 Appium 1.5
  • Maven 生成系统(版本 3.3.9 及更高版本)

更改生成系统

必须先添加依赖关系,以确保生成系统可执行准备必要文件的任务,然后才能开始使用 Test Cloud。

添加 Test Cloud 依赖关系:若要在项目中添加 Test Cloud,并确保增强型 Android 和 iOS 驱动程序在编译时可用,请向 pom.xml 文件添加以下依赖关系:

<dependency>
  <groupId>com.xamarin.testcloud</groupId>
  <artifactId>appium</artifactId>
  <version>1.0</version>
</dependency>

添加上载配置文件:将图 1 中的配置文件添加到 pom.xml 文件中的 <profiles> 标记内。如果还没有 <profiles> 部分,请先创建一个,然后再添加配置文件。此配置文件将测试类和所有依赖关系打包到目标/上载文件夹中,从此文件夹可以将它们上载到 Test Cloud 中。

图 1:Test Cloud 上载配置文件

<profile>
  <id>prepare-for-upload</id>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.10</version>
        <executions>
          <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>${project.build.directory}
                /upload/dependency-jars/</outputDirectory>
              <useRepositoryLayout>true</useRepositoryLayout>
              <copyPom>true</copyPom>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-resources-plugin</artifactId>
        <executions>
          <execution>
            <id>copy-pom-file</id>
            <phase>package</phase>
            <goals>
              <goal>testResources</goal>
            </goals>
            <configuration>
              <outputDirectory>${project.build.directory}
                /upload/</outputDirectory>
              <resources>
                <resource>
                  <directory>
                    ${project.basedir}
                  </directory>
                  <includes>
                    <include>pom.xml</include>
                  </includes>
                </resource>
              </resources>
            </configuration>
          </execution>
          <execution>
            <id>copy-testclasses</id>
            <phase>package</phase>
            <goals>
              <goal>testResources</goal>
            </goals>
            <configuration>
              <outputDirectory>${project.build.directory}
                /upload/test-classes</outputDirectory>
              <resources>
                <resource>
                  <directory>
                    ${project.build.testOutputDirectory}
                  </directory>
                </resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</profile>

更改测试

现在,你已配置生成系统,必须修改测试类才能利用 Test Cloud Java 扩展。

向测试类添加导入内容:将以下包导入测试类:

import com.xamarin.testcloud.appium.Factory;
import com.xamarin.testcloud.appium.EnhancedAndroidDriver;
import org.junit.rules.TestWatcher;
import org.junit.Rule;

实例化 TestWatcher:将此实例化插入其中一个测试类:

@Rule
public TestWatcher watcher = Factory.createWatcher();

更新驱动程序声明:将每个 AndroidDriver <MobileElement> 声明替换为 EnhancedAndroidDriver­<MobileElement>,如下所示:

private static EnhancedAndroidDriver<MobileElement> driver;

更新驱动程序实例化:替换每个驱动程序实例化,这样以下格式的各行代码:

Driver = new AndroidDriver<MobileElement>(url, capabilities);

将变成:

Driver = new EnhancedAndroidDriver<MobileElement>(url, capabilities);

借助增强型驱动程序,你可以使用 driver.label(“myTestStepLabel”) 来“标记”测试中的步骤。此方法将生成测试步骤标签,并随附屏幕截图,以供你可以在 Test Cloud 测试报告中查看。我建议在 @After 方法中调用标签,这样可以捕获测试完成前应用程序的最终状态屏幕截图。即使测试失败,也会捕获屏幕截图,其中可能包含有关失败原因的重要数据分析。实际情况可能如下所示:

@After
  public void tearDown(){
    driver.label("Stopping app");
    driver.quit();
  }

上载到 Test Cloud

现在,项目已满足所有先决条件,你可以准备文件并在 Test Cloud 中运行测试了。最好先尝试本地运行测试,确保一切如预期一样正常运作后,再继续执行上载步骤。如果需要对刚才进行的任何配置更改进行故障排除,请在本地执行,速度要快得多。

若要将测试类和所有依赖关系打包到目标/上载文件夹中,请运行以下命令:

mvn –DskipTests -P prepare-for-upload package

不妨验证目标/上载目录现在是否存在于项目的根文件夹中,以确保你已做好上载准备。如果这将是 Test Cloud 中的新应用,则需要在测试运行时创建应用。按照以下流程新建一个测试运行,以便选择设备、设置首选项,并生成运行测试所需的命令。对于这项操作,我建议从层 1 类别中选择少量的设备,以便可以快速审核结果。复制生成的命令,然后在命令行中运行。

在成功议定和验证文件上载后,将预配设备、安装应用并执行测试。Test Cloud 运行模式依据的是设备并发或可以并行使用的物理设备的数量。例如,具有五个并发设备的用户可以同时在 Nexus 5X、Nexus 6P、Samsung Galaxy S5、Samsung Galaxy S6 和 HTC M8 上测试应用。高效是 Test Cloud 最显著的优势之一,你可以轻松地覆盖更多设备,几乎不会延长等待时间。

命令行工具会流式更新测试运行状态,然后在测试运行完成后提供测试报告链接。单击所提供的测试报告链接可查看结果。

可以查看以下三种粒度级别的结果:

  • 概述报告。
  • 设备网格。
  • 设备详情报告。

我将挨个介绍。

概述报告:概述报告中包含测试运行的摘要信息,包括通过/失败详细信息、按操作系统版本、制造商、外形规格统计的故障信息。此外,还包含测试运行本身的详细信息,包括目标设备配置和总运行时长(见图 2)。

概述报告
图 2:概述报告

如果测试运行失败,你很有可能想要更深入发掘根本原因,并收集数据以供调试。设备网格就是粒度级别更为精细的一种报告。

设备网格:设备网格建立了一种高效机制,便于你浏览每一步的测试结果,以每一步捕获的屏幕截图。这种报告明确指出哪一步失败了,以便你可以快速跳到失败的测试步骤,然后直观地检查应用在每个设备上的状态。对于较大的设备集,可以从显示的设备中筛选出无法创建更清晰的字段以供检查的设备。如果这种粒度级别仍无法凸显失败原因,可以使用粒度级别更为精细的报告,查看设备详情(见图 3)。

设备网格报告
图 3:设备网格报告

设备详情报告:在设备详情视图中,你仍可以跳转查看测试步骤和屏幕截图。除此之外,你还可以查看选定设备的其他详情,包括 CPU 和内存使用情况。在此视图中,你还可以查看在调查测试失败原因时可能最有用的设备日志、堆栈跟踪和项目(见图 4)。

设备详情报告
图 4:设备详情报告

此时,我已执行最常见的 Test Cloud 工作流:

  1. 执行测试(手动或通过持续集成 (CI))。
  2. 查看结果。
  3. 检索调试项目。
  4. 修复。

接下来,我将介绍几种简单的方法,指导你如何制定设备定位策略,以及如何优化测试工作流的性能以确保管道数据流快速流动。

确定设备覆盖面

选择组织支持并最终测试的设备几乎与测试本身一样重要。虽然有许多来源提供汇总的通用市场数据,有助于你思考这一方面的问题,但最重要的来源还是你自己的用户群的使用情况数据。当然,仅向内部一组已知的托管设备发布的应用程序除外。对于外部应用和使用者应用,以及根据“自带设备办公 (BYOD)”策略发布的内部应用,使用情况数据是最佳数据来源。

市场上的许多工具都可以帮助你了解自己的受众所使用的设备。这就是可以作为支持设备列表的扩展依据的数据集。具体采用哪种方法来确定支持汇总列表中的哪些设备由你和你的组织决定。大多数情况下,根据使用情况数据选择支持所有设备毫无意义,因为这样做很快就会导致数据庞大复杂且费用高昂。不妨决定根据各设备用户在用户群中所占的比例,覆盖相同比例的设备。或者,不妨决定从用户数这一角度出发,根据需要覆盖尽可能多、但少于 500 台的用户设备。如果你有电子商务应用,不妨参照使用情况数据和交易数据,以确保覆盖支出最高且交易最频繁的设备。同样,用于确定设备支持列表的具体方法应以业务需求和目标为依据。

请注意,移动市场发展迅速。也就是说,若要确定准确且有意义的支持列表,必须定期检查使用情况数据。关注市场信号,把握再次检查数据的绝佳时机,如推出新的设备型号或操作系统。

优化测试管道

发掘测试自动化的最大价值的最好方式是尽早频繁测试。这样可以减少与修复错误相关的时间和成本,并确保部署管道的畅通。不过,随着团队和运营规模的扩大,管道中的延迟可能会增加,开发者的工作效率可能会降低。让我们来看看如何保持管道畅通和很高的工作效率。

并非所有测试都一样:随着项目在一段时间内不断发展,测试套件的运行时间也会变长。转折点之后,在执行简单的更改后运行测试套件会变得棘手和繁琐,通常会导致坏习惯的养成,如完全跳过测试。可以提前确定应用程序的关键路径(即应用程序中绝对必须采用的流或体验),从而预先制止这样的情况发生。 以之前提到的电子商务应用为例,这可能是指用户可以浏览产品、将产品添加到购物车中并结帐。用户能否设置通知首选项不太重要。使用这种结构,在每次推送或甚至每次提交后运行测试的可行性更大。不必为了细微更改而运行整个测试套件,只需运行关键路径中的一部分即可。具体如何实现这一描述视你使用的测试框架而定。

在合适的时间测试适当的设备:虽然从质量角度来看测试每次推送到功能分支的内容可能最为理想,但对于大型团队(特别是支持许多不同设备配置的团队)来说,这样做很快就会产生高昂费用。在这种情况下,可以向这些测试运行的目标设备应用渐进式策略,从而降低开销。是否需要对支持的所有设备都测试生成的非生产分支? 答案可能是否定的。相反,可以选择合理数量的设备,通过缩短等待时间来平衡有效测试。对于通过 CI 生成的预生产版本,从设备支持列表中抽取最常用的设备型号和操作系统版本,既能提供有价值的覆盖水平,又能保证生成时间增加不超过一小时。对于从本地工作站运行测试的开发者个人,测试一到两个设备可能就足够了。

这些只是在确定如何配置测试工作流时可采用的部分方法。更主要的方法还是花时间问问自己管道流是否最优。即使你以前检查过管道流是否最优,最好还是要定期检查和调整,就像你做的其他任何事情一样。

总结

本文介绍了如何从在模拟器或单个本地设备上运行测试轻松转变为使用 Xamarin Test Cloud 覆盖数百个设备配置。我还介绍了几种策略,指导你如何组织测试工作流和发掘测试资源的最大价值。如果尚未使用 Test Cloud,可以在 bit.ly/2e3YgTy 上注册获取免费试用版,立即开始在你的项目中使用它。


Justin Raczak是 Microsoft 的高级项目经理,负责领导移动测试自动化服务团队。虽然他是最近才加入 Microsoft,但他在过去三年里一直专注于研究自动化测试服务及其在推进持续交付方面所起到的作用。可通过 justin.raczak@microsoft.com 与他联系。

衷心感谢以下 Microsoft 技术专家对本文的审阅: Simon Søndergaard
Simon Søndergaard 是 Microsoft 的软件工程师。