美文网首页软件测试Git技术文
[译]Netflix设备的自动化测试

[译]Netflix设备的自动化测试

作者: 微笑0619 | 来源:发表于2016-11-17 15:48 被阅读535次

    原文地址:Automated testing on devices

    译者:杰微刊兼职译者macsokolot(@gmail.com)    

    作为NetflixSDK工作团队的一员,我们的职责是确保Netflix应用新版本都经过了彻底的测试,在部署到游戏终端上或是作为一个SDK分发到(可作为一个参考样例程序)Netflix设备的合作伙伴之前保证其高运行质量;并最终向智能电视的百万和机顶盒子(set top boxes,STB’s)推送。总的来说,我们的测试是保证在数以百万计游戏终端以及通过互联网连接的电视/机顶盒中的Netflix运行质量。

    不同于服务端的软件分发方式,客户端软件分发所特有的挑战在于在程序崩溃的情况下并没有红/黑推送(red/black pushes)或立即回滚功能。如果客户端有一个BUG,那么从代码修正到客户端推送,修复这个BUG的代价将非常之高。Netflix不得不重新与众多合作商重新对他们的设备进行再次认证,尽管他们的设备可能已经被Netflix认证过,但只要修复版本一推送,这个流程就得重新走一遍,这样整个项目的时间花费无论是外部还是内部都是十分巨大的。与此同时,用户对这个BUG将束手无策,随之而来的便是糟糕的Netflix用户体验。那么避免此类问题出现的最好办法就是:在版本推送之前确保应用的每项测试都能引导到设备之中,然后检测应用的回归测试是否达到要求。

    这是关于自动测试系统一系列文章中的第一部分,阐述了我们在多个设备上用于自动化功能、性能和压力测试Netflix SDK的关键概念和基础框架。

    期许目标(Aspirational Goals)

    多年来,我们使用手动和自动化手段测试Netflix应用程序的经验告诉我们几个教训。 因此,是时候重新设计我们的自动化系统以进入下一个级别和规模,我们需确保将这些定为核心目标。

    低设置成本/高测试“灵活性”(Low setup cost / High test “agility”)

    当使用自动化才测试时,测试不应难以创建和/或使用。特别是在能够简单地手动运行的测试应该也能在自动化中运行并保持简单化。这意味着使用自动化工具应该具有接近零的设置成本(如果存在)。确保创建新的测试,和调试现有的测试应该是快速和无缝的,这非常重要。在测试中,这也确保了重点尽可能长地保持在测试和功能上。

    无测试结构约束

    使用自动化系统不应限制于特定格式编写的测试。重点在于这样做便于未来能够创新测试的编写方式。此外,不同的团队(我们与负责平台,安全,播放/媒体/ UI等的团队进行了交流)可能会提出不同的方法来构建测试,以更好地满足他们各自的需求。确保自动化系统与测试结构解耦从而提高其可重用性。

    测试层尽量少的抽象层(Few layers at the test level)

    当构建大规模系统时,很容易终止于太多的抽象层。虽然这在许多情况下这样做并不是不好,但是当这些层也被添加到测试中以便能让它们与自动化集成时,这就会出现问题。事实上,这会导致你离实际需要测试的功能越来越远,并且在问题出现时将很难调试:在被测的应用程序之外的很多调用可能会出错。

    在我们的案例中,我们在设备上测试Netflix,因此我们要确保在设备上运行的调用函数尽可能靠近正在测试的SDK功能。

    支持重要的设备功能(Support important device features)

    手动完成设备管理需要消耗大量时间,因此这是一个优秀的自动化系统的应该占的很大组成部分。 由于我们测试正在开发的产品,因此我们需要能够即时更改构建并将其部署到设备。 提取日志文件和崩溃转储对于自动化也非常重要,以便将调试测试失败过程能按流水线处理。

    设计自动化(Designing automation)

    通过这些目标,我们的团队需要一个提供必要的自动化和设备服务的系统,同时尽可能地避免与测试模块耦合。

    这需要重新思考现有框架并创建一种新型的自动化生态系统。为了使自动化具有这种灵活性,我们需要自动化系统是精细的,模块化的,并且只有在确实需要测试某个功能时才需要外部服务,也就是说只有当某个功能不能从设备上的应用直接完成才会请求外部服务(例如挂起应用程序或操纵网络)。

    将外部服务的使用量减少到最低限度有几个好处:

    1、它尽可能地确保关于测试的逻辑驻留在测试本身之内。这提高了测试的可读性,维护和可调试性。

    2、大多数测试最终结束于没有外部的依赖上,这使开发人员能够不再重新配置就能重现一个bug然后测试,而绝对不是去用他们之前使用的工具去配置然后运行这个测试。

    3、测试用例编写者可以专注于测试设备的功能,而不必担心外部约束。

    在最简单的层面上,我们需要有两个单独的实体:

    1、测试框架

    一种软件抽象,通过公开控制测试流程的功能来帮助编写测试用例。

    测试框架就是帮助编写测试,编写的测试应尽可能接近正在测试的设备/程序底层,这样能够在调试一个测试失败的时候减少需要转换的模块。

    测试框架可能有很多,以便不同的团队可以按照他们的需要来组织他们的测试。

    2、自动化服务

    一组外部后端服务能够帮助管理设备,自动执行测试,以及在迫切需要时提供用于测试的外部功能。 自动化服务应该尽可能以最独立的方式构建。减少服务之间的依赖关系能提升更好的可重用性,降低维护、调试和功能迭代所需的成本。例如,启动测试的服务、收集关于测试运行的信息、验证测试结果可以委托给单个微服务辅助实现。这些微服务有助于独立地运行测试,而不需要运行一个测试自动化服务。自动化服务只提供服务,而并不能控制整个测试流程。

    例如,测试可以请求外部服务重新启动设备作为测试流程的一部分。但是服务不能命令这个测试去重新启动设备或控制整个测试流程。

    建立即插即用生态系统(Building a Plug and Play Ecosystem)

    当涉及到设计自动化服务时,我们就需要了解每个服务需要什么。

    1、设备管理

    虽然测试本身是自动的,但在各种设备上进行测试需要一些自定义步骤,例如在测试开始之前刷入固件,升级和启动应用程序,以及在测试结束后收集日志和崩溃转储信息。这些操作中的各个步骤可能在不同设备上操作完全不同。因此,我们需要一个服务来提取设备特定信息,并为不同设备提供一个通用接口

    2、测试管理

    编写测试只是整个测试过程中的一小部分;但也需要注意以下几点:

    - 对测试进行编组(测试套件)

    - 选择何时运行它们

    - 选择要运行它们的配置如何

    - 存储测试结果

    - 可视化测试结果

    3、网络操作

    在带有波动带宽的设备上测试Netflix应用程序体验的核心要求是确保高质量不间断的播放体验。我们需要一种服务可以改变网络状况,包括流量整形和DNS操纵。

    4、文件服务

    当我们开始收集用于归档目的或用于存储巨大日志文件的构建时,我们需要一种方式来存储和检索这些文件,并且通过文件服务以实现这一点。

    5、Test Runner

    每个服务是完全独立的,因此我们需要一个协调器,能够与各个独立的服务进行通信,以便在测试运行之前获取和准备设备,并在测试结束后收集测试结果数据。

    遵循上述的设计思想,我们构建了以下自动化系统。

    下面所描述的服务(services)为满足上述特定需求,其设计的原则:尽可能独立,而不是将其绑定到测试框架中。这些概念在实践中如下所述。

    设备服务

    设备服务抽象化了从头到尾管理设备所需的技术细节。所有类型的设备都统一通过RESTful接口进行接入,设备服务的用户不再需要掌握任何设备特定的知识:它们可以使用所有或者任一设备,就像它们是相同的,设备之间没有差异。

    管理每个类型的设备的方法并不是在设备服务本身中直接实现,而是授权给device handlers程序代理,而这个device handlers属于一个独立微服务(micro-services)。

    这提高了对新类型设备支持的灵活性,因为设备处理程序(device handlers)可以根据他们自己选择的REST API以任何编程语言编写实现,并且现有处理程序(handlers)可以容易地与设备服务集成。一些处理程序(handlers)有时可能需要与设备进行物理连接,因此将设备服务(device service)与设备处理程序(device handlers)分离,在提高了灵活性同时也能方便快速定位到它们。

    对于接收到的每个请求,设备服务的功能是找出哪个设备处理程序来联系和代理这个请求,找到之后,由device handler与对应的一组REST API进行交互。

    让我们来看一个更具体的例子...例如,在PS4上安装build的操作与在Roku上安装bulid非常不同。 一个依赖于用C#编写的代码,它与运行在Windows(相对于PlayStation)上的ProDG Target Manager交互,而另一个运用Node.js中的ProDG Target Manager在Linux系统中进行交互。而PS4和Roku设备处理程序(device handlers)都实现了各自设备特定安装过程。

    如果设备服务需要与设备通信,则设备服务需要知道设备特定信息。每个设备具有唯一标识符,且被存储成可由设备服务访问的设备映射对象(device map object),包含了关于handler所需的设备的信息。例如:

    1、设备IP或主机名

    2、设备Mac地址(可选)

    3、处理程序IP或主机名

    4、处理程序端口

    5、Bifrost IP或主机名(网络服务)

    6、Powercycle IP或主机名(远程电源管理服务)

    首次将设备添加到我们的自动化中时,需要填充设备映射信息。

    当引入用于测试的新设备类型时,由设备服务实现和公开该设备的特定处理程序(handler)。 设备服务支持以下常见的设备方法集:

    注意,这些每个端点都需要在请求中嵌入唯一的设备标识符。此标识符(类似于序列号)需要绑定到正在操作的设备。

    保持服务简单性使得其高可拓展性。给设备添加附加功能也可以很容易地实现,如果设备不支持某个功能,你只需简单地将其置为空。

    设备服务还充当设备池:

    下面是我们在实验室中运行的一些自动化设备的一些图片。注意看Xbox 360电源按钮附近有个小机械手。这是一个自定义解决方案,我们只是把Xbox 360放在一起,因为此设备需要手动按键才能重新启动它。我们决定自动化这个手动过程,通过设计一个机械臂连接到raspberry pi,它能发送控制命令使机械手移动并按下电源按钮。此操作已添加到Xbox 360的设备处理程序。设备服务的powercycle端点调用Xbox 360的电源循环处理程序。此操作对于PS3或PS4不是必需的,并且不在这些设备处理程序(handler)中实现。

    测试服务:

    测试服务是运行测试用例会话的记录员。其目的是标记测试用例的开始,记录状态变化,日志消息,元数据,文件的链接(在整个测试期间收集的日志/小型崩溃数据)以及由测试用例发出的数据集,直到测试完成记录结束。该服务记录由运行测试用例的测试框架调用的单个端点操作:

    通常测试框架会内部调用这些端点操作,如下:

    1、一旦测试开始,就开始调用POST / test / start。

    2、周期性保持发送POST / test / keepalive操作,让测试服务知道测试正在进行。

    3、测试信息和结果在测试运行时使用POST / test / configuration和POST / tests / details发送

    4、当测试结束时,调用POST / test / end

    网络服务 - Bifr?st桥

    我们构建的网络系统,用于与设备通信,进行流量整形或dns操作被称为Bifr?st桥。我们不改变网络拓扑,并将设备直接连接到主网络。对于Bifrost桥不需要运行测试,而只有在某个测试需要网络操作(如覆盖DNS记录)时才需要。

    文件服务

    如果我们正在运行某个测试,我们可以选择收集测试生成的文件,并通过文件服务将它们上传到存储仓库。这些包括设备日志文件,崩溃报告,屏幕截图等...从使用这个服务的用户角度来看,这个服务非常简单:

    文件服务是Varnish Cache提供技术支持,由云存储和资源的文件服务被缓存用于快速检索。

    数据库

    我们选择使用MongoDB作为测试服务的首选数据库,因为它的JSON格式和它在无模式(schema-less)方面的优点。开放式JSON文档存储解决方案所带来的灵活性是满足我们需求的关键,因为测试结果和元数据存储始终在不断迭代,并且在其结构上并不是固定的。 虽然从数据库管理角度来看,关系数据库听起来颇具吸引力,但它违背了了即插即用(Plug-and-Play)的原则,因为数据库事务(schema)需要随时随地进行任何测试。

    当运行在CI 模式下,我们记录每个测试唯一的运行ID,并收集有关build配置信息,设备配置信息,测试细节等。可下载的链接到文件服务日志也存储在数据库测试条目中。

    Test Runner — Maze Runner

    为了减少每个测试用例所有者调用不同服务并单独运行测试的负担,我们构建了一个控制器称为Maze Runner,它协调运行测试和调用所需的不同服务。

    测试套件的所有者创建一个脚本,其中他/她指定需要运行测试的设备(或设备类型)、测试套件名称和形成测试套件的测试用例,并请求Maze Runner执行测试(并行地)。

    下面是Maze Runner所做的操作列表:

    1. 根据请求查找要运行的设备/设备

    2. 调用设备服务以安装build

    3. 调用设备服务以启动测试

    4. 等待测试在测试服务中标记为“已结束”

    5. 显示使用测试服务检索的测试结果

    6. 使用设备服务收集日志文件

    7. 如果测试没有启动或没有结束(超时),Maze Runner将使用设备服务检查应用程序是否崩溃。

    8. 如果检测到崩溃,它会收集coredump,生成调用堆栈并通过专有调用堆栈分类器运行并检测崩溃签名

    9. 如果发生崩溃或超时,通知测试服务。

    10. 在序列操作的任何时候,如果Maze Runner检测到设备有问题(例如因为设备丢失了其网络连接,build将不会安装或设备将不会启动),它将释放设备,并将此设备服务禁用一段时间,直到最终得到一个全新的设备来运行测试。这个想法的核心是纯硬件故障不应该影响测试。

    测试框架

    测试框架与自动化服务完全分离,因为测试框架在设备本身中运行。大多数测试可以手动运行,无需自动化服务。这是系统设计的核心原则之一。 在这种情况下,需手动启动测试,并在完成测试时手动检索和结果检查。

    然而,可以将测试框架与自动化服务(例如,用于存储测试进度和结果的测试服务)结合在一起操作。当测试在CI中并由我们的运行器运行时,我们需要这种与自动化服务集成。

    为了以灵活的方式实现这一点,我们创建了一个内部称为TPL(测试可移植性层)的抽象层。 TPL为每个自动化服务定义简单的接口以便测试和测试框架能调用到此层中。每个自动化服务可以提供这些接口的具体实现。

    这一层允许能够在我们的自动化系统中运行的测试也能够在另一个完全不同的自动化系统中执行,只要这个系统中的服务实现了TPL接口。这使得使用由其他团队编写的测试用例(使用不同的自动化系统)能够在原封不动的情况下运行它们。 当一个测试不变时,由测试所有者对设备上的测试失败(test failure)进行故障排除时,之间的障碍被完全消除; 我们总是想保持这种工作方式。

    进展

    在保持测试框架独立于自动化服务之下,我们设法在基础需求之上使用自动化服务以添加缺少的设备功能:

    1. 拓宽我们的游戏控制台和参考应用程序的测试自动化覆盖面。

    2. 将基础架构(infrastructure)扩展到移动设备(Android,iOS和Windows Mobile)。

    3. 使其他质量保证部门能够利用我们的设备基础架构建立他们的测试和自动化框架。

    最近我们的测试执行覆盖率数据显示,我们的参考应用程序(reference applications)能够单独为每个build执行大约1500次测试。为了说明问题,开发团队为参考应用程序每天在一个分支(branch)上生成大约10-15个build,每个build生成5种不同的build版本(例如Debug,Release,AddressSanitizer等)。 对于游戏终端,每天有大约3-4个build具有单一的build风格。 保守地说,使用单个build风格时,我们的生态系统负责在给定的一天运行的测试用例量接近1500 * 10 + 1500 * 3 =?20K。

    新挑战

    鉴于每天执行的测试数量众多,出现了两个突出的挑战:

    1. 设备和生态系统的可扩展性和弹性挑战

    2. 由测试结果产生遥测分析过载挑战

    在未来的博文中,我们将更深入地探讨我们在为解决这些巨大的新挑战而采取的广泛的举措。

    更多精彩译文

    相关文章

      网友评论

        本文标题:[译]Netflix设备的自动化测试

        本文链接:https://www.haomeiwen.com/subject/raidpttx.html