在CI服务器上运行单元测试有什么意义?


98

为什么要在CI服务器上运行单元测试?

当然,当某些事情要掌握时,开发人员已经在之前运行了所有单元测试,并修复了新代码可能发生的任何错误。这不是单元测试的重点吗?否则,他们只会提交损坏的代码。


51
我们的开发人员不允许致力于掌握。他们推送到功能分支,然后CI服务器与master合并并运行测试。如果成功,更改将合并到主服务器。因此测试失败的代码无法掌握...
蜘蛛侠鲍里斯(Boris)

2
@BoristheSpider-确实非常好的工作流程。master应始终保持理智,最好在每次合并时自动部署到用于内部质量检查和测试的过渡环境。
Per Lundberg

130
“当然,当某些事情要掌握时,开发人员已经在之前运行了所有单元测试,并修复了新代码可能发生的任何错误。” 您生活在哪个幻想世界中?
jpmc26 2016年

5
在某些行业中,重要的部分不仅是对代码运行测试,而且还对二进制文件运行测试。在CI输出上运行测试意味着您可以保证所交付的产品能够正常工作,因为您的客户收到的确切二进制文件就是通过所有测试的二进制文件。听起来很琐碎,但有时可能会产生影响(我见过的就是混淆;在复杂的项目上,或者奇怪地进行设置,可能会造成混淆版本中的问题,而干净版本中没有这些问题)。
anaximander

5
“当然,当某些事情要掌握时,开发人员已经在运行所有单元测试,并修复了新代码可能发生的任何错误。” ...不确定是否很严重
chucksmash '16

Answers:


223

当然,当某些事情要掌握时,开发人员已经在之前运行了所有单元测试,并修复了新代码可能发生的任何错误。

或不。发生这种情况的原因可能有很多:

  • 开发人员没有纪律去做
  • 他们忘记了
  • 他们没有提交所有内容,而是推送了不完整的提交集(感谢Matthieu M.
  • 他们只运行了一些测试,但没有运行整个套件(感谢nhgrif
  • 在合并之前,他们在分支机构进行了测试(感谢nhgrif * 2)

但是真正的重点是在不是开发人员机器的机器上运行测试。一种配置不同的文件。

这有助于找出测试和/或代码依赖于特定于开发人员的工具(配置,数据,时区,语言环境等)的问题。

CI构建运行测试的其他良好理由:

  • 在除主要开发平台以外的其他平台上进行测试,这对于开发人员可能很难做到。(感谢TZHX
  • 接受/集成/端到端/真正长时间运行的测试可能在CI服务器上运行,而通常不会在开发人员盒上运行。(感谢Ixrec
  • 开发人员可能会在推送/提交之前进行一些微小的更改(认为这是安全的更改,因此不运行测试)。(感谢Ixrec * 2)
  • CI服务器配置通常不包括所有开发人员工具和配置,因此更接近生产系统
  • CI系统每次都从头开始构建项目,这意味着构建是可重复的
  • 库的更改可能会导致下游问题-CI服务器可以配置为构建所有相关的代码库,而不仅仅是库

36
其他常见原因:1)CI服务器可能会运行高级集成/验收测试,而这对于开发人员来说总是太长时间了。2)开发人员确实运行了它们,然后做了一个微小的更改,然后才推动他们非常确定不会破坏任何东西,但是我们希望确定。
Ixrec

11
对依赖项的更改通常也会同时运行所有下游构建。如果开发人员所做的更改使下游中断,则在修改库时不容易看到(例如,将基础数据类型从SortedSet更改为HashSet(仅提供Set的约定),而下游人员则错误地假设:集已排序)。如果不在CI服务器上运行(下游)测试,则该错误会持续一段时间。

2
@MichaelT好抓住。这实际上是这些天我们> 90%的CI失败的原因,不确定我是怎么忘记的...
Ixrec

34
同样,在CI环境中运行它们通常意味着您从头开始设置项目,以确保构建是可重复的
mgarciaisaia

5
同样,可以提交两项更改,这些更改可以分别测试,但是可以合并在一起(例如,其中一项删除未使用的API,另一项开始使用它)。
西蒙·里希特

74

作为一个在提交源代码管理之前不会运行所有集成和单元测试的开发人员,我将在这里提出自己的辩护。

我将必须构建,测试和验证应用程序在以下位置正确运行:

  • 带有Visual Studio 2008编译器的Microsoft Windows XP和Vista。
  • 带有Visual Studio 2010编译器的Microsoft Windows 7。
    • 哦,MSI会为每个构建。
  • RHEL 5和6分别具有4.1和4.4(类似CentOS)
    • 7很快。呼呼呼啦。
  • 带有GCC的Fedora工作站最新三个最新版本。
  • 最近三个最新版本的Debian(以及Ubuntu等衍生产品)。
  • 最近三个最新版本的Mac OSX。
    • 和软件包(rpm,dmg等)

添加Fortran(具有Intel和GNU编译器),Python(以及不同版本,具体取决于操作系统)和bash / bat脚本组件,嗯,我认为您可以看到事情逐渐发展起来

这就是我必须拥有的16台机器,每天只能运行几次测试。仅为此管理基础结构,这几乎是一项全职工作。我认为几乎所有人都会同意这是不合理的,尤其是将其乘以项目中的人数。因此,我们让CI服务器完成工作。

单元测试不会阻止您提交损坏的代码,它们会告诉您是否知道您已损坏了某些内容。人们可以说“单元测试应该是快速的”,并继续讨论原理,设计模式和方法论,但实际上有时让我们为重复性,单调任务设计的计算机能够更好地完成这些任务,并且只有在它们参与进来后,告诉我们他们发现了一些东西。


3
单元测试测试代码而不是配置。如果添加新测试并将其扔到墙上而又没有先在本地运行它,那将是非常不明智的做法
Robbie Dee

33
@RobbieDee恐怕看不到你的意思?我建议不要在没有本地测试的情况下创建新测试,或者只是在没有自己对其进行测试的情况下就盲目地将其提交给源代码控制,而我在自己的计算机上运行测试-但是“配置”的确需要进行测试以确保行为一致,并且最好还是在开发人员的想法仍在这一领域时相对快地执行此操作,而不是在主要使用Mac的团队醒来四千英里并更新其副本时发现问题。
TZHX

7
@RobbieDee我想说的是,如果TZHX可以在本地运行所有测试,但是他们不能。由于TZHX无法进行,因此他们在本地运行一些测试(例如,那些可以在其开发系统上运行并且与更改后的代码足够短或最相关的测试),然后让整个电池在CI系统上运行。相当合理。
muru

11
@RobbieDee:他相信单元测试。因此,他在Macbook air上对其进行了测试,然后通过并签入。运行Red Hat,Solaris和Windows的CI服务器将再次运行这些测试。知道您测试的内容也可以在生产平台上使用是不是很高兴?
slebetman '16

2
@RobbieDee:我经常编写特定于特定平台上特定编译器的单元测试。例如,考虑一个图形子系统,该子系统使用AMD(英特尔竞争对手)特定的CPU指令,这些指令仅在g ++(GNU C ++编译器)4.5版或更高版本上可用,但是我碰巧在Atom CPU和ICC(Intel C ++)上工作编译器)。每次在该机器上运行AMD / g ++ 4.5-tests都是胡说八道,但这是要在发布之前进行测试的代码。再加上我自己的独立于CPU的代码,必须对其进行正确的互操作性测试。当然,有虚拟机和仿真器,...
phresnel

23

除了出色的Oded答案:

  • 您可以从存储库中测试代码。它可能会在您的计算机上处​​理您的文件,而您忘记了提交。它可能取决于没有创建脚本(例如,在liquibase中),一些配置数据或属性文件的新表。
  • 您可以避免代码集成问题。一个开发人员下载最后一个版本,创建单元和集成测试,添加代码,在其计算机中通过所有测试,提交并推送。另一个开发人员也做了同样的事情。两种更改本身都是正确的,但是合并时会导致错误。这可能是存储库合并,或者仅仅是未将其检测为冲突。例如,开发人员1删除完全不使用的文件。开发人员2针对该文件进行编码,并在不更改开发人员1的情况下进行测试。
  • 您开发一个脚本以从存储库自动部署。拥有通用的构建和部署脚本可以解决许多问题。一些开发人员可能添加了并非所有人共享的lib或编译选项。这不仅节省了您的时间,而且更重要的是,它使部署安全且可预测。此外,您可以在存储库中回到2.3.1版,并使用与此版本兼容的脚本来部署此版本。它包括数据库对象,例如视图,存储过程,视图和应进行版本控制的触发器。(否则您将无法返回可行的版本)。
  • 其他测试:像集成,性能和端到端测试。这可能很慢,并且可能包括Selenium等测试工具。您可能需要具有真实数据库的完整数据集,而不是模拟对象或HSQL。

我曾经在一家由于合并和部署过程而存在很多部署错误的公司工作。这是由于一个奇怪的专有框架导致测试和CI变得困难。发现无法在开发中完美使用的代码并没有投入生产,这不是一个快乐的经历。


是的,只是忘记提交某些更改是很常见的。我会说忘记“ svn添加”新文件,因此忘记以后提交它们是获得自动构建失败的最流行的方法。
Sharptooth '16

22

您会以为不是-但是开发人员是人,有时会忘记。

同样,开发人员经常无法提取最新代码。他们的最新测试可能运行良好,然后在签入时,其他人做出了重大更改。

您的测试也可能依赖于本地(未签入)资源。您的本地单元测试无法进行。

如果您认为以上所有都是幻想,那么在CI之上(至少在TFS上)有一个称为Gated的级别,在该级别中,具有失败测试的构建将被搁置,并且不提交给代码库。


7
我看到了更多糟糕的事情,我忘记了我愿意承认的CI失败。
Dan Neely

@DanNeely坦白地说,这会让构建经理踢屁股,因为您忘了告诉他/她一些事情... :-)
Robbie Dee

3
这就是我喜欢CI的原因之一。找到并修复您自己的糟糕比让别人找到您更好。
Dan Neely

14

等到某个东西掌握了

我通常将CI配置为在每次提交时都运行。在测试分支之前,分支不会合并到master中。如果您依赖在master上运行测试,那么这将打开一个窗口,以破坏构建。

在CI机器上运行测试是关于可重现的结果。由于CI服务器具有从VCS提取的已知干净环境,因此您知道测试结果是正确的。在本地运行时,您可能会忘记提交通过它们所需的一些代码,或者忘记了使它们在失败时通过的未提交代码。

通过并行运行不同的套件,它还可以节省开发人员的时间,特别是如果某些缓慢,耗时数分钟的测试,并且每次更改后不太可能在本地运行。

在我目前的工作中,我们的生产部署是通过CI并通过所有测试的。除非通过脚本,否则部署脚本将阻止部署。这使得不可能意外忘记运行它们。

CI作为工作流的一部分,也减轻了开发人员的负担。作为开发人员,您通常是否对每一个更改都运行lint,静态分析器,单元测试,代码覆盖率和集成测试?CI可以完全自动且无需考虑-减少决策疲劳。


1
您不应该真正进行缓慢的单元测试-这违反了FIRST原则。
罗比·迪

4
@RobbieDee:我认为通常CI服务器运行所有测试,而不仅仅是单元测试。
RemcoGerlich

4
@RobbieDee:理论上所有单元测试都是快速的。实践中...无论如何,CI可以并且应该运行所有测试-棉绒,静态分析,单元测试,集成测试。
丹妮丝

2
@RobbieDee显然,具体的配置因团队而异。即使构建需要花费几分钟,通常也可以并行运行多个构建。给定一个单一的代码库,这可能是一个更大的缺点,但IME并不是障碍。
丹妮丝'16

1
@RobbieDee我认为这更多地取决于您的体系结构。我已经看到它适合80左右的工程团队使用,但这是针对产品领域的明确定义的子团队。
丹妮丝

4

等到掌握一些东西时,开发人员应该已经运行了所有单元测试……但是如果没有,该怎么办?如果您不在CI服务器上运行单元测试,那么直到其他人将所做的更改拉到他们的计算机上并发现测试刚刚在他们身上中断后,您才能知道。

另外,开发人员可能犯了一个错误,并引用了特定于其计算机的本地资源。当他们签入代码并且CI运行失败时,该问题将立即被识别并可以纠正。


3

假设(与其他答案相反),开发人员的纪律严明,并且在提交之前进行了单元测试,可能有以下几个原因:

  • 对于某些特殊设置,运行单元测试可能需要很长时间。例如,使用内存检查器(如valgrind)运行单元测试可能需要更长的时间。尽管所有单元测试都通过了,但是内存检查可能会失败。
  • 对于某些特殊设置,结果并不那么重要-例如,运行单元测试以检查代码覆盖率需要特殊的编译标志。对于普通开发人员而言,代码覆盖范围并不那么重要-对于谨慎的人而言,代码保持一定的质量(例如团队领导)更为重要。

3

可以想象这样的情况:变更A不会破坏测试,而变更B不会破坏测试,但是A和B 一起破坏测试。如果A和B是由不同的开发人员制作的,则只有CI服务器会检测到新错误。A和B甚至可能是同一较长句子的两个部分。

想象一下由两个机车A和B驾驶的火车。也许一个绰绰有余,这就是适用的解决方法。但是,如果同时应用了两个“修复”功能,则火车将不会移动。

同样,并非所有开发人员都运行所有单元测试,而大多数优秀开发人员都可以运行。


2

让我们问一个等效的问题:

为什么要在CI服务器上构建代码?

当然,当某些事情要掌握时,开发人员已经在之前构建了代码,并修复了新代码可能发生的任何错误。这不是构建代码的重点吗?否则,他们只会提交损坏的代码。


进行CI的原因有很多,但CI的要点是了解一段时间后代码的状态。这提供的主要好处(从几个方面中提供)是,我们可以找出构建中断的时间,找出破坏该构建的原因,然后进行修复。

如果代码永不中断,为什么还要使用CI?要交付测试版本,每晚构建就足够了。

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.