下载本文的PDF版本 PDF

编排自动化测试实验室

编写乐谱可以帮助我们管理分布式应用程序测试的复杂性。

MICHAEL DONAT,SILICON CHALK

网络和互联网正在鼓励人与其软件之间更高程度的互动和协作。无论用户是在玩游戏还是编写法律文件,他们的应用程序都需要管理来自多台机器的动作在可能不可靠的连接上的复杂交织。例如,Silicon Chalk 是一个分布式应用程序,旨在增强教师和学生的课堂体验。其分布式特性要求我们使用多台机器进行测试。手动测试过于繁琐、昂贵且不一致,无法有效进行。然而,在自动化我们的测试时,我们发现维护一组描述每台机器在给定测试中所占部分的脚本非常耗费人力。可维护性受到影响,因为测试描述分散在多个文件中。

经验使我确信,测试分布式软件需要一个自动化测试实验室,其中每个测试描述都指定了跨多个测试实验室组件的行为。

在本文中,我探讨了对中心化测试描述(称为乐谱)的需求。我描述了乐谱的语法,作为讨论我遇到的问题以及一些解决这些问题的方法的一种手段。这些想法基于我们在 Silicon Chalk 为尽可能集中自动化我们的测试所做的努力。尽管其他应用程序可能需要不同的测试实验室基础设施,但我概述了我认为的核心问题。

聚集

在 Silicon Chalk,我们需要一个大型测试实验室。这仅仅部分是因为我们需要使用各种硬件测试我们的应用程序。我们的软件专为课堂使用而设计,许多学生与教师互动。班级可能包含 20 到 200 台通过有线或无线网络连接的笔记本电脑。Silicon Chalk 支持面对面课堂中的协作、交流、练习、笔记和演示,其中部分或全部学生拥有笔记本电脑、台式机或平板电脑。一台参与者的机器上的动作可能对所有其他机器产生影响的方式有很多种。所有相互通信的多个 Silicon Chalk 实例的集体动态行为要求在多实例环境中对其进行测试,而不是测试单个实例。

这是分布式应用程序与更传统的独立应用程序(如 Excel)之间的根本区别。例如,同时使用 Excel 的两个用户无需知道彼此的存在。他们可能对彼此产生的唯一影响是通过争用外部资源(例如,电子表格)。这种争用通常可以通过任意分配资源直到资源空闲并可以传递来解决。相比之下,Silicon Chalk 的多个实例创建了一个聚集,其中每个实例都有既得利益,需要以协调的方式将信息放到网络上。

在 Silicon Chalk 会话中,信息主要从教师流向学生。例如,教师展示一组幻灯片,它们会逐个出现在学生的显示器上。在会话的其他时间,信息可能会从学生流回教师(例如,学生对测验的答案)。因此,在任何给定时间,都可能有很多机器试图将数据放到网络上。

特别是,无线网络非常脆弱,并且在接近容量时可能会开始泄漏数据。当聚集的成员在网络上发送数据时,他们必须注意泄漏了多少数据。请注意,Silicon Chalk 是分布式应用程序的一个示例,其中某些资源(例如,无线网络)必须作为聚集而不是单独进行管理。

我们执行的最重要的测试类型之一是组测试,它模拟了通常在通常通过无线网络连接的许多机器的集合上进行的典型会话。我们模拟各种会话活动并测量网络利用率以验证性能。我们在不同数量的机器和网络配置(例如,仅 802.11b、b+g、仅 g、单接入点、多接入点等)上进行这些测试。

各种无线网络设备可能或可能不会对网络性能产生影响(从 Silicon Chalk 的角度来看)。不同的网络硬件有可能以不同的速率传输数据。因此,不同的实例将具有不同的数据传输特性,从而导致不同的行为。必须调查这些交互,以确保拥有各种学生笔记本电脑的校园能够进行令人满意的 Silicon Chalk 会话。

由于 Silicon Chalk 是许多机器并行交互,因此测试变得复杂。每种配置都需要有机会充当教师和学生。Silicon Chalk 工具在会话期间的交互也增加了要测试的潜在案例的数量。

自动化

手动完成所有这些测试是一项可怕的工作。自动化是唯一可行的选择。重要的是要认识到自动化不仅限于执行被测应用程序。有几个方面的测试需要考虑,这些方面通常适用于分布式应用程序

组件配置。 测试实验室包含各种设备,包括计算机和用于测试的网络设备。组件需要在测试开始之前进行配置。这包括网络设置和计算机上使用的操作系统/软件镜像。

构建安装。 大多数现代软件开发项目都有自动化构建系统。一旦构建完成,自动化测试就变得有意义。在此之前,必须将最新的成功构建安装到测试实验室中的所有计算机上。

给定组件上的测试执行。 我将操作与组件而不是与应用程序关联,因为 Silicon Chalk 也与其他应用程序交互。因此,操作可能引用其他应用程序,并且不限于 Silicon Chalk。教师可能会向学生展示另一个应用程序,或者学生机器上的课堂管理工具可能会向教师报告打开的应用程序。因此,在机器上执行的操作可能包括在 Silicon Chalk 启动之前和之后打开和驱动其他应用程序。机器上的代理负责处理该机器的脚本。

此外,某些操作可能会发生在诸如接入点之类的硬件上。例如,这将使我们能够确保 Silicon Chalk 在无线网络退化的情况下优雅地降级。

跨多台机器同步操作。 为了可重复性,必须同步在不同机器上执行的操作。例如,学生在收到测验之前无法开始回答测验。

测试后日志文件分析。 测试结束后,需要收集和分析数据以确定测试的成功与否。即使会话看起来正常进行,也可能存在性能问题。

构建安装和测试后分析通常对于每个测试都是相同的,因此特定测试的规范需要包括组件配置以及一组操作以及这些操作的同步方式。在本文的剩余部分,我将重点介绍指定同步操作。

编写同步操作脚本的一种自然方法是为每台机器编写一个脚本,并将同步点编码到每个脚本中。同步点就像脚本中的门。只有在所有代理都到达该点之后,才能处理在同步点之后编写脚本的操作。

然而,每个测试有多个脚本会产生一个问题:它使测试难以维护。主要问题是可读性。通过查看多个来源来创建测试实验室中正在发生的事情的精神图像非常麻烦。可以使用多个脚本来完成,但这是一种变得乏味且容易出错的方法。更好的解决方案是以一种方式编写一个脚本,该脚本不仅编码了在每台机器上执行的操作,还编码了这些操作是如何同步的。

乐谱

让我们考虑一下引用多个测试实验室组件的脚本的一些理想属性。我将这样的脚本称为乐谱。(请参阅“乐谱语言结构”侧边栏。)乐谱不仅需要描述一系列步骤,还需要描述在不同测试实验室组件上并行发生的事情。测试实验室组件可以是机器、接入点、交换机等。任何可以自动操作且与测试相关的任何东西都应该可以在乐谱中编写脚本。乐谱在服务器(指挥者)上执行,指挥者控制测试实验室。

为了可维护性,乐谱首先必须是可读的,并且必须引用逻辑组件而不是物理组件。这允许一个乐谱应用于更广泛的配置。乐谱可能包含一个显式进行组件映射的前言,或者它可能是算法完成的。无论哪种方式,乐谱的主体都应仅包含逻辑引用。

关键点是逻辑引用实际上可能引用一组机器。我们使用这种方法来模拟许多学生在不同的计算机上并行执行任务。指令块可以分配给非空组件集,该组件集被赋予一个名称(例如,“乐谱语法”侧边栏中的 groupA)。此属性使乐谱具有通用性和容错性。乐谱的编写假设在任何给定的测试运行中,groupA 可能有不同的物理成员。对于 Silicon Chalk,有多台学生机器,但只有一个教师。通过使用教师和学生组编写脚本,我们具有通用性,可以使我们测试实验室中的每台机器依次充当教师。

故障

在测试期间,测试实验室的任何组件都可能发生故障。需要识别这些故障,以便可以确定原因。手动监控测试实验室的所有组件是不可行的,因此需要自动检测错误并记录以供以后分析。一个组件上的故障并不意味着测试失败。实际上,某些测试可能旨在在各种组件上引起故障行为,以便测试鲁棒性。

重要的是要区分组件上的可恢复故障、组件的故障和测试的故障之间的区别。只要测试实验室中存在最小的功能组件集,继续进行测试就有价值。由于我们希望通过测试发现尽可能多的故障,因此最好继续进行测试,直到最小集不再存在。

必须考虑确定测试何时失败,以便可以重置测试实验室并部署下一个测试。由于脚本的编写假设每个组至少有一个成员,因此合理地假设继续测试是有价值的,直到遇到一个操作,使得指定组之一不再有成员。

例如,如果教师失败,Silicon Chalk 测试基本上就死了,继续进行测试毫无意义。但是,如果一个学生失败,我们希望继续进行测试,以便捕获测试可能在其他机器上发现的任何其他故障。测试后日志文件分析将提醒我们注意早期的学生故障。即使发生多个学生故障,只要至少有一个学生和一个教师,测试就可以继续进行。以这种方式编写乐谱使我们能够隐式地指定所需的最小测试实验室组件集,并且此集合可以随着测试的进行而更改。

组件故障意味着什么? 无法再信任失败的组件按预期执行。一旦发生这种情况,重要的是将组件从测试中删除,因为它有可能将虚惊一场引入测试结果。

指挥者必须以某种方式识别故障。这可以通过某些组件内部的方式(例如,异常)来实现,也可以通过某些组件外部的方式(例如,活性检测器)来实现。某些故障可能意味着当前操作未成功,但组件仍然能够运行。其他故障更为严重,代表组件故障。这两种类型都可以通过异常来处理。

例如,考虑乐谱片段

   try {
          [groupA] try {
                   A
                   B
          }
          catch (local_exception) {
                    C
          }
   }
   catch (component_exception) {
          D
   }

乐谱定义了可能在不同组件上运行的代码。在此示例中,内部 try-block 在 groupA 组件上执行。请注意,D 超出了 groupA 指定的范围,因此如果 C 抛出异常,D 将在指挥者 D 上执行。在 groupA 组件上的 A 或 B 期间发生的本地故障将导致组件在 C 处继续,但指挥者不会执行 D,因为组件本身没有失败。更严重的异常将被最外层的 try-block 捕获,指挥者将执行 D,并且测试将继续。如果没有最外层的 try-block,乐谱将失败,并且将部署下一个测试。

不可重复性

理想情况下,我们希望测试实验室中的所有组件都在精确的正确时间执行其操作,以确保测试真正可重复。在调试时,我们希望能够重复观察有问题的行为,以检查故障的不同方面。不幸的是,当我们增加测试实验室中组件的数量时,当我们尝试重现错误时,我们会失去可重复性。即使我们能够跨测试实验室组件重新创建正确的刺激,重新创建精确的同步状态也是非常困难的。

在指挥者发出操作开始信号和代理在组件上启动操作之间存在很小的延迟。这意味着,尽管我们希望组件启动延迟尽可能短,但预计会有一些非零延迟。导致此延迟的因素包括:组件上不同的操作系统、不同的附加软件、不同的硬盘碎片、不同的处理器速度和不同的内存。

由于控制这些变量是不切实际的,我发现实际上希望为每次测试运行假设这些变化。这使我们有信心,随着时间的推移,我们的测试涵盖了更现实的条件数量。由于我们不太可能完全重现给定的测试运行,这意味着我们需要依赖于检测应用程序,以便我们在查找故障源时可以尽可能多地掌握信息。日志应包含足够的信息,以便软件开发人员可以理解发生故障的上下文。信息的某些示例包括代码路径标签和性能值。

截止时间

乐谱语法没有说明给定组件上的执行何时开始,也没有说明组件上的执行将花费多长时间。保证的只是同步点。这就提出了截止时间问题。我们等待组件到达同步点多长时间?在某种程度上,我们需要确定并施加一个截止时间,我们可以使用该截止时间来判断组件是否失败。

如果组件在同步点错过了截止时间,则将其从测试中删除。实际上,由于组件现在与乐谱不同步并且在乐谱的假设之外运行,因此相应的代理必须停用该组件,使其无法污染进一步的测试结果。

在 Silicon Chalk,我们的脚本语言基于在规定的时间间隔内发生的用户界面事件。这使得计算组件响应的截止时间变得简单。截止时间只是如果没有事件发生,则下一个事件将发生的时间。

由于我们假设应用程序可以在脚本中指定的时间间隔内处理用户界面事件,因此任何需要发生的额外等待都已明确指定。对于其他脚本环境,其他方法可能更可行。您可能希望明确指定持续时间。然而,测试实验室硬件不可避免的升级可能会使这种方法非常耗费人力。

统计方法可能更有效。需要的是一种简单的方法来指定操作完成时间分布的容忍度。例如,假设指挥者配置为容忍比基于收到的完成时间计算的平均操作持续时间长 10% 的完成时间。如果指挥者知道它从一个组中的 10 个组件代理收到了操作启动,并且最近收到了 8 个完成,那么它可以不断重新计算其自身的截止时间,并在平均持续时间的 1.1 倍过去后认为某些组件失败。请注意,指挥者必须任意等待第一个组件响应。

为了避免无限期地等待单个失败的组件,可以使用适当大的默认截止时间。或者,可以为测试分配一定量的时间。如果超过时间限制,则终止测试。我们的 Silicon Chalk 测试使用时间限制加上用户界面事件计划方法,尽管我认为平均响应因子方法更可取。毕竟,可以设想一种增强的语法,该语法允许可选地指定平均等待因子和硬截止时间。

例如,考虑 [groupA] 1.5 10 S。这将意味着 groupA 的成员最多有 10 秒的时间向指挥者发送操作完成响应,并且还必须击败比当前收到的响应的平均值快 1.5 倍的截止时间。假设 groupA 有四个成员:A1、A2、A3 和 A4。如果 A1 在 4 秒标记处响应,则除非 A2、A3 和 A4 中至少有一个在 6 秒之前响应,否则它们将被视为失败。假设 A2 在 5 秒时响应。现在 A3 和 A4 的截止时间已移至 6.75 秒。如果 A3 在 6 秒时响应,则 A4 的截止时间为 7.5 秒。

截止时间容忍度可以很容易地变得更严格。考虑前一个示例,截止时间平均因子为 1.05。如果 A1 在 4 秒时响应,则必须在 4.2 秒之前收到至少一个更多响应。

限制和挑战

设计良好的测试实验室可以经济地解决为其设计的软件的大部分测试需求,但总是有局限性。由于实际和经济原因,我选择不使用 Silicon Chalk 测试实验室来解决许多场景。一些示例包括:大量笔记本电脑在大量接入点上进行多项活动;以及与其他软件和无线网络硬件配置的详尽交互。

对于诸如 Silicon Chalk 之类的聚集应用程序,传统的测试脚本编写具有挑战性,因为测试的描述不是中心化的。一种特殊的脚本,我称之为乐谱,它编码了脚本和同步信息,将解决这个问题。乐谱还增加了脚本化测试的通用性和容错性。

在实施基于乐谱的测试实验室时出现的问题包括

在本文中,我根据我的 Silicon Chalk 观点提出了一些对这些问题的解答。我希望不同的产品会偏爱不同的答案集。

乐谱语言结构

此处的示例说明了本文中讨论的语言结构。

下面的函数 test1(右侧)指定了在一个测试期间要执行的顺序和并行操作。将有几个这样的函数,每个函数代表一个单独的测试。

在测试规范之上是在测试之前关于测试实验室设置的指令(install-build),一个执行测试的部分(test1、test2),以及最后收集结果并创建摘要的部分。

try <response-average-deadline-factor=5 timelimit=20*60*1000> {
      [laptops] {
           reimage(“imagefile-\1”);
           install-build();
      }
 }
 tests = new Array(test1, test2); // test2 defined elsewhere
 try <response-average-deadline-factor=1.5> {
      for test in tests {
           test();
      }
 }
 try <response-average-deadline-factor=3 timelimit=4*60*1000> {
      [laptops] reboot();
      sleep(3 * 60 * 1000);
      [laptops] report-log-summary(“\1”);
      create-summary-html();
 }
 function test1() {
      [instructor, students] Login();
      par {
           [instructor] {
                StartCourse(course);
                OpenPresentationHost();
                SetPresentationModeFollowTop();
                StartPresentation();
                Browser.open(“http://www.cbc.ca”);
                Sleep(10 * 1000);
                Browser.EnterField(“search”, “quirks”);
                Sleep(5 * 1000);
                Browser.Click(“Go”);
           }
           [students] {
                JoinCourse(course);
                OpenPresentationClient();
           }
      }
      Sleep(5 * 1000);
      [Instructor] PowerPointShow(“test.ppt”); 
      // contains 25 slides, slide transitions every 12 seconds
      [students] SendQuestion(“This is a test question ” + computername);
      [instructor, students] {
           LeaveCourse();
           Quit();
      }
 }


乐谱语法

要指定一系列操作,我们可以使用在
大多数编程语言中发现的正常方法。顺序

   A
   B
   C

当然,这意味着 A 之后是 B,然后是 C。
要指定操作并行发生,我们可以使用诸如

Par {
      A
      B
      C
     }

这意味着操作 A、B 和 C 在同一时间开始。

要指定哪个组件执行操作,我们可以简单地使用将执行操作的逻辑组件列表标记操作。

[groupA] A
[groupB] B
[groupA, groupB] C
par {
      [groupA] A 
      [groupB] B
}

在每个顺序操作(A-B、B-C、C-par)之间都存在隐式同步点。占位符 A、B 等中使用的脚本或编程语言是无关紧要的。实际上,根据目标组件(可能具有不同的
代理脚本引擎),可能希望允许多种语言。

MICHAEL DONAT (www3.telus.net/donat) 是加拿大不列颠哥伦比亚省温哥华市 Silicon Chalk Inc. 的质量保证主管。他对软件开发问题的兴趣始于 1987 年至 1992 年在 Microsoft 担任软件设计工程师期间。他获得了不列颠哥伦比亚大学的博士学位,他的论文重点是根据形式化需求集自动生成测试。

acmqueue

最初发表于 Queue 第 3 卷,第 1 期
数字图书馆 中评论本文





更多相关文章

Sanjay Sha - 企业应用程序的可靠性
企业可靠性是一门学科,它确保应用程序在不损害可用性、性能和可维护性等核心方面的情况下,以一致、可预测且经济高效的方式交付所需的业务功能。本文介绍了一组企业可以应用的核心原则和工程方法,以帮助他们驾驭企业可靠性的复杂环境并交付高度可靠且经济高效的应用程序。


Robert Guo - MongoDB 的 JavaScript Fuzzer
随着时间的推移,MongoDB 变得更加功能丰富和复杂,开发更复杂的方法来查找错误的需求也在增长。三年前,MongDB 将一个本土的 JavaScript fuzzer 添加到其工具包中,它现在是我们最多产的错误查找工具,负责在两个发布周期内检测到近 200 个错误。这些错误跨越了从分片到存储引擎的各种 MongoDB 组件,症状从死锁到数据不一致不等。fuzzer 作为 CI(持续集成)系统的一部分运行,它经常捕获新提交代码中的错误。


Robert V. Binder, Bruno Legeard, Anne Kramer - 基于模型的测试:它的现状如何?
您可能听说过 MBT(基于模型的测试),但像许多未使用 MBT 的软件工程专业人员一样,您可能对其他人使用这种测试设计方法的经验感到好奇。从 2014 年 6 月中旬到 2014 年 8 月初,我们进行了一项调查,以了解 MBT 用户如何看待其效率和有效性。2014 年 MBT 用户调查是 2012 年类似调查的后续调查,向所有评估或使用过任何 MBT 方法的人开放。它的 32 个问题包括在 2013 年高级自动化测试用户会议上分发的一项调查中的一些问题。一些问题侧重于 MBT 的效率和有效性,提供了管理者最感兴趣的数据。


Terry Coatta, Michael Donat, Jafar Husain - EA 的自动化 QA 测试:由事件驱动
对于数百万游戏发烧友来说,在 Electronic Arts 担任 QA(质量保证)测试员的职位似乎是一个梦想的工作。但从公司的角度来看,与 QA 相关的开销可能看起来非常可怕,尤其是在大型多人在线游戏时代。





© 保留所有权利。

© . All rights reserved.