您如何扩展集成测试?


21

我正在研究用于扩展我们当前产品上不断增加的集成测试数量的技术和策略,以便使它们(人类)能够成为我们开发和CI流程的一部分。

在大约200多个集成测试中,我们已经达到1小时的标准以完成完整的测试运行(在台式机上运行),这对开发人员在常规推送过程中容忍运行整个套件的能力产生了负面影响。这正影响着积极地去管教他们创造良好的动机。我们只对关键的场景进行前后集成测试,并且我们使用的环境可以反映生产,该环境是在每次测试运行时从头开始构建的。

由于运行需要时间,因此无论测试运行有多集中,都会造成严重的反馈循环和许多浪费的周期,等待机器完成测试运行。别介意对流程和进度,理智和可持续性造成更大的负面影响。

我们希望在该产品开始变慢之前进行10倍以上的集成测试(虽然还不知道,但似乎还没有开始使用功能)。我认为在某些时候,我们必须合理地期望要进行几百或几千个集成测试。

明确地说,要防止这种情况成为关于单元测试和集成测试的讨论(永远不要交易)。我们正在使用TDD进行单元测试,并在该产品中进行集成测试。实际上,我们在服务体系结构的各个层进行集成测试,这对我们有意义,因为我们需要验证在将体系结构中的模式更改为其他领域时,我们在何处引入重大更改。系统。 

关于我们的技术栈的一些知识。我们目前正在(CPU和内存密集型)仿真环境上进行测试,以从头到尾运行我们的测试。由组成noSql后端(ATS)的Azure REST Web服务组成。我们通过在Azure桌面模拟器+ IISExpress中运行来模拟生产环境。每台开发机仅限于一个模拟器和一个本地后端存储库。

我们也有一个基于云的CI,它可以在相同的仿真环境中运行相同的测试,并且与我们当前的CI提供程序一起在云中进行测试所花费的时间是其两倍(2小时以上)。就硬件性能而言,我们已经达到了云CI提供程序SLA的极限,并且超出了他们在测试运行时间上的允许范围。为了公平起见,他们的规格还不错,但显然是内部脏台式机的一半。

我们正在使用一种测试策略,为每个逻辑测试组重建数据存储,并预加载测试数据。在全面确保数据完整性的同时,这对每个测试增加了5-15%的影响。因此,我们认为在产品开发的这一点上优化该测试策略几乎无济于事。 

总而言之,它的缺点是:尽管我们可以优化每个测试的吞吐量(即使每个测试的吞吐量提高多达30%-50%),但在不久的将来,我们仍然无法通过数百个测试有效地扩展规模。现在1小时甚至还远远超出了人类可以忍受的范围,我们需要在整个过程中进行一定程度的改进以使其可持续。

因此,我正在研究可以采用哪些技术和策略来大大减少测试时间。

  • 编写更少的测试不是一种选择。让我们不要在这个线程中争论那个。
  • 尽管价格昂贵,但绝对可以选择使用更快的硬件。
  • 无疑,在并行环境中在单独的硬件上运行测试/方案组也是肯定的选择。
  • 围绕正在开发的功能和场景创建测试分组是合理的,但最终无法证明完整的覆盖范围或对系统不受更改影响的信心。 
  • 从技术上讲,可以在云规模的暂存环境中运行而不是在台式机模拟器中运行,尽管我们开始将部署时间添加到测试运行中(在测试运行开始时每个部署时间大约20分钟以部署内容)。
  • 将系统的组件分成独立的逻辑部分在一定程度上是合理的,但是由于组件之间的干扰预计会随着时间而增加,因此我们认为在此方面的里程有限。(即,更改是无效的,可能会以意想不到的方式影响其他人,这在系统逐步开发时经常发生)

我想看看其他人在这个领域使用什么策略(和工具)。

(我必须相信其他人在使用某些技术集时可能会遇到这种困难。)

[更新:2016年12月16日:我们最终在CI并行测试上投入了更多资金,以讨论结果:http//www.mindkin.co.nz/blog/2015/12/16/16-jobs]


自撰写本文以来,我已经研究过nCrunch(我们在单元测试中广泛使用)可能是一种可以为我们提供策略的工具。显然,它具有将测试发送到远程计算机并并行运行它们的能力。因此,确定集成测试组以及多个高规格云计算机实例可能是一件值得尝试的事情?nCrunch声称这是此功能的确切意图。还有其他人尝试过吗?
Jezz Santos'5

看来这正在讨论什么是集成测试,什么不是集成测试,以及人们对单元测试和集成测试的误解,天哪!
杰兹·桑托斯

Answers:


9

我在一个需要5个小时(跨30台机器)的地方工作,以运行集成测试。我重构了代码库,并为新内容进行了单元测试。单元测试花费了30秒(在一台机器上)。哦,虫子也掉了。和开发时间,因为我们确切地知道细粒度测试会导致什么失败。

长话短说,你没有。随着代码库的增长,完整的集成测试呈指数级增长(更多的代码意味着更多的测试,而更多的代码意味着随着要进行的更多“集成”工作,所有测试的运行时间将更长)。我会争辩说,“小时”范围内的任何内容都会失去持续集成的大部分好处,因为反馈回路不存在。即使仅提高一个数量级,也不足以使您获得良好的表现-而且距离您的扩展能力还差得很远。

因此,我建议将集成测试缩减为最广泛,最重要的烟雾测试。然后,它们可以每晚运行或间隔不连续一些,从而减少了很多性能需求。单元测试只是随着您添加更多代码而线性增长(测试增加,而每次测试运行时却没有),这是扩大规模的方法。


我同意。单元测试具有更大的可扩展性,并支持更快的反馈循环。
布兰登

8
您可能已经错过了这一点。OP已经进行了广泛的uint测试以及相关的集成测试。单元测试绝不能替代集成测试。不同的工具,不同的做法,不同的目的,不同的结果。这绝不是一个问题。
耶兹·桑托斯

1
在帖子中增加了清晰度,以明确说明我们使用TDD来构建此产品,因此我们已经有成千上万的单元测试,并由相关的集成测试提供支持。。
杰兹·桑托斯

8

集成测试应始终运行很长时间,因为它们应模仿真实用户。因此,您不应该同时运行它们!

考虑到您已经在云中运行了某些东西,在我看来,您处于在多台计算机上扩展测试的最佳位置。

在极端情况下,每次测试启动一个新环境并同时运行它们。这样,您的集成测试将只花费最长的运行测试时间。


好主意!着眼于这样的策略,但使用了一些有助于分布式测试的工具
Jezz Santos

4

缩减/优化测试对我来说似乎是最好的主意,但是如果这不是一个选择,我可以提出一个替代方案(但需要构建一些简单的专有工具)。

我遇到了类似的问题,但是在我们的集成测试中却没有(这些问题在几分钟内就解决了)。取而代之的是,它只是在我们的构建中:大规模的C代码库需要花费数小时才能构建。

我看到的极为浪费的事实是,即使仅更改了几个源文件,我们也从头开始重建了整个内容(大约20,000个源文件/编译单元),因此花了数小时进行更改,而这只需要几秒钟或几分钟的时间最坏的情况

因此,我们尝试在构建服务器上进行增量链接,但这是不可靠的。有时会产生假阴性,并且无法在某些提交上继续构建,仅在完全重建后才能成功。更糟糕的是,有时仅给开发人员将已损坏的构建合并到主分支中,否则有时会产生误报并报告构建成功。因此,每当开发人员从其私人分支机构进行更改时,我们便重新构建所有内容。

我非常讨厌这个。我会走进一半的开发人员玩视频游戏的会议室,只是因为等待构建时别无其他事情。我尝试通过多任务处理并在提交后启动新分支来提高生产力,以便我可以在等待构建的同时处理代码,但是当测试或构建失败时,将更改排入队列将变得非常痛苦并尝试修复所有问题并将其缝合起来。

等待时进行侧项目,稍后集成

因此,我要做的是为应用程序创建一个骨架框架-相同类型的基本UI和SDK的相关部分,让我可以针对整个单独的项目进行开发。然后,我将在主项目外部等待构建时针对此代码编写独立代码。至少这给了我一些编码,以便使我保持较高的工作效率,然后,我将开始将完全在产品外部完成的工作集成到项目中-即代码片段。如果开发人员发现自己等待很多,那将是一种策略。

手动解析源文件以找出要重建/重新运行的内容

但是我讨厌我们如何浪费这么多时间一直在重建所有内容。因此,我花了两个周末来编写一些代码,这些代码实际上将扫描文件中的更改并仅重建相关的项目-仍是完全重建,没有增量链接,而仅重建需要重建的项目(其依存文件(递归解析)已更改)。那是完全可靠的,并且在进行了详尽的演示和测试之后,我们能够使用该解决方案。由于我们仅重建必要的项目,因此平均构建时间从数小时缩短到了几分钟(尽管中央SDK的更改可能仍需要一个小时,但与本地更改相比,这样做的频率要低得多)。

相同的策略应适用于集成测试。只需递归地解析源文件以找出集成测试所依赖的文件(例如:import在Java中,#include在C或C ++中),并从这些文件中包含/导入文件,依此类推,从而为系统构建完整的包含/导入依赖文件图。与构成DAG的构建解析不同,该图应该是无向的,因为它对任何包含可以间接执行*的代码的已更改文件感兴趣。仅在图形中感兴趣的集成测试的那些文件中的任何一个已更改时,才重新运行集成测试。即使是数百万行代码,也可以在不到一分钟的时间内完成解析。如果除了源代码之外还有其他文件可能会影响集成测试,例如内容文件,则可以将元数据写入源代码中的注释中,以指示集成测试中的那些依赖项,以便在这些外部文件发生更改时,测试也重新运行。

*例如,如果test.c包含foo.h,而foo.c也包含foo.h,则对test.c,foo.h或foo.c的更改应将集成测试标记为需要重新运行。

这可能需要一整天或两天的时间来进行编程和测试,尤其是在正式环境中,但是我认为即使对于集成测试也应该工作,并且如果您别无选择,只能等待几个小时进行构建,那是值得的完成(由于建造,测试或包装过程或其他原因)。这可能意味着在短短几个月内就损失了很多工时,这将使构建这种专有解决方案所需的时间相形见war,并浪费了团队的精力,增加了大笔合并中的冲突所带来的压力由于浪费所有时间而频繁地等待。当整个团队花费大量时间等待事情时,这对整个团队来说都是不好的。每次更改时,所有要重建/重新运行/重新打包的内容。


3

听起来您有太多的集成测试。召回测试金字塔。集成测试位于中间。

作为一个实例采取与方法的库set(key,object)get(key)。该存储库在整个代码库中得到了广泛使用。依赖此存储库的所有方法都将使用伪造的存储库进行测试。现在,您只需要两个集成测试,一个用于设置,一个用于获取。

其中一些集成测试可能会转换为单元测试。例如,在我看来,端到端测试应仅测试使用正确的连接字符串和正确的域正确配置了站点。

集成测试应测试ORM,存储库和队列抽象是否正确。根据经验,集成测试不需要域代码-只需抽象。

几乎所有其他内容都可以使用存根/模拟/伪造/内存中实现的依赖项进行单元测试。


1
有趣的观点。我们的集成测试并非试图验证每个ReST调用的每个参数的每个排列。在我们看来,这不是集成测试。他们正在通过API运行关键的端到端方案,从而打击了各种后端存储和其他系统。目的是确保随着API的更改,它们可以确定需要注意哪些方案(即不再按预期工作)。
杰兹·桑托斯

1
我们在体系结构的各个级别上进行了集成测试。在您的示例中,我们对访问数据存储的类进行了单元测试,因此我们知道它们对数据存储进行了正确的调用,我们进行了集成测试以建立存储副本并测试它们是否正确读写数据与商店。然后,我们在REST API中使用这些数据类,这些数据类是我们通过单元测试创​​建的,然后是集成测试,用于启动Web服务并进行调用以确保数据从头到尾一直传递,反之亦然。您是否建议我们在这里进行太多测试?
Jezz Santos'5

我更新了我的回答作为对您评论的回应。
Esben Skov Pedersen

2

以我在敏捷或DevOps环境中的经验(在这些环境中,连续的交付流水线很常见),应该在完成或调整每个模块时进行集成测试。例如,在许多连续交付管道环境中,每个开发人员每天要进行多次代码部署并不少见。在这种类型的环境中,在每个开发阶段的末尾运行一组快速的集成测试应该是一种标准实践。有关更多信息,您可以阅读有关此主题的精彩电子书,其中包括Katrina Clokie撰写的《DevOps中的实用测试指南》

为了以这种方式有效地进行测试,必须在专用测试环境中针对现有的完整模块对新组件进行测试,或者对存根和驱动程序进行测试。根据您的需求,通常最好在文件夹或库中为每个应用程序模块保留一个存根和驱动程序库,以实现快速重复的集成测试使用。像这样保持存根和驱动程序井井有条,可以轻松地执行迭代更改,使它们保持更新并以最佳性能运行,从而满足您不断进行的测试需求。

要考虑的另一种选择是最初在2002年左右开发的解决方案,称为服务虚拟化。这将创建一个虚拟环境,模拟与现有资源的模块交互,以在复杂的企业DevOps或敏捷环境中进行测试。

本文对于了解更多有关如何在企业中进行集成测试的知识很有用。


尽管这是可行的(如果可以将系统拆分成这样的模块,但并非所有产品都可以)-曾经是很久以前的规范,但它实际上延迟了集成,因此失去了CI / CD的所有优势。Kinda敏捷,您不觉得吗?在此类集成测试中发现的问题无法轻松快速地与特定提交匹配,因此需要从头开始进行全面调查,就像生产中产生的错误一样(而且您知道修复这些错误的成本要高得多)。
Dan Cornilescu

1

您是否测量了每个测试以查看时间在哪里?然后,如果速度特别慢,则测量代码库的性能。整体问题是测试之一还是部署之一,还是两者兼而有之?

通常,您希望减少集成测试的影响,以使在相对较小的更改上运行它们的可能性降到最低。然后,您可以保留对“ QA”运行的完整测试,该测试将在分支提升到下一个级别时执行。因此,您具有针对dev分支的单元测试,在合并时运行简化的集成测试,在合并到候选发布版本时运行完整的集成测试。

因此,这意味着您不必重新构建,重新打包和重新部署每次提交的所有内容。您可以在开发环境中组织安装程序,以尽可能便宜地执行部署,并相信它可以。例如,与其拆分整个VM并部署整个产品,不如将VM保留旧版本并复制新的二进制文件(例如,YMMV取决于您的工作)。

这种整体乐观的方法仍然需要全面测试,但是可以在较不紧急的情况下在稍后阶段执行。(例如,您可以在晚上运行一次完整的测试,如果开发人员可以在早晨解决这些问题,则可以进行一次测试)。这还具有在集成平台上刷新产品以进行第二天测试的优势-随着开发人员对事物的更改,它可能会过时,但仅需1天。

运行基于安全性的静态分析工具时,我们遇到了类似的问题。全面运行将需要很长时间,因此我们将其运行从开发人员提交转移到集成提交(即,我们有一个系统,开发人员说完成了该工作,将其合并到一个“二级”分支,在其中执行了更多测试,包括性能测试)。测试完成后,将其合并到质量检查分支以进行部署,其目的是删除夜间进行的定期运行的定期运行-开发人员将在早上获得结果,并且不会影响开发专注于他们的开发周期的后期)。


1

在某个时候,即使在昂贵的硬件上,全套集成测试也可能需要花费数小时才能完成。一种选择是不对每个提交运行大多数测试,而是每天晚上或以连续批处理模式运行(每多次提交一次)。

但是,这带来了一个新问题-开发人员不会立即收到反馈,而且损坏的版本可能不会引起注意。要解决此问题,重要的是,他们必须始终知道某些东西已损坏。诸如CatlightTeamCity的托盘通知程序之类的构建通知工具可能非常有用。

但是,还会有另一个问题。即使开发人员发现构建已损坏,他也可能不急于检查它。毕竟,可能已经有人在检查它了,对吧?

因此,这两个工具具有“构建调查”功能。它会告诉开发团队中是否有人正在检查并修复损坏的构建。开发人员可以自愿检查构建,直到发生这种情况时,团队中的每个人都会被时钟旁的红色图标烦恼。


0

听起来您的代码库越来越大,某些代码管理会有所帮助。我们使用Java,因此如果我假设这样做,请提前道歉。

  • 需要将一个大型项目分解为可编译为库的较小的单个项目。诸如nexus之类的Java工具使此操作变得容易。
  • 每个库都应实现一个接口。这有助于在更高级别的测试中存出库。如果库访问数据库或外部数据存储(例如大型机),则此功能特别有用。在这种情况下,使大型机或数据库数据进入可重复状态可能很慢,并且可能是不可能的。
  • 每个库的集成测试都可以进行全面的测试,但仅在提交新库源时才需要运行。
  • 更高级别的集成测试应仅调用这些库并假定它们是完美的。

我工作的Java商店使用这种方法,因此很少等待集成测试运行。


谢谢,但是我认为我们在这种情况下对集成测试的目的和应用没有相同的理解。您可能将集成测试与单元测试混为一谈。
杰兹·桑托斯

0

保留执行时间长或需要有限和/或昂贵资源的CI管道集成测试(或任何类型的验证,包括构建)的另一种可能方法是,基于提交后的验证(即易发生拥塞)(基于提交前的验证)。

开发人员无需将其更改直接提交给分支机构,而是将其提交到集中的自动验证系统,该系统执行验证并:

  • 如果成功,它将自动将更改提交到分支
  • 如果未成功,则通知相应的提交者重新评估其更改

这种方法允许将多个提交的更改组合在一起并进行测试,从而有可能多次提高有效CI验证速度。

这样的例子之一就是OpenStack使用的基于Gerrit / Zuul的门控系统

另一个是ApartCI免责声明 -我是它的创建者,也是提供它的公司的创始人)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.