2009-06-18 235 views
27

目前我们的项目有超过3000个单元测试,“ant testAll”需要超过20分钟。 除了获得更好的硬件,有没有办法加快速度?你如何加快Java单元测试?

+0

你应该更具体地说明什么是举办这些测试。 – starblue 2009-06-18 07:42:41

+0

我两个月前加入了这个项目,它已经有了一个庞大的代码库。 它不是一个web应用程序,除了一堆xml配置文件,它并不经常执行File IO。 我们正在使用我们定制的TestRunner。但事情是我们只为系统开发插件,因此我们没有TestRunner的源代码。我假设TestRunner的实现者足够聪明,可以优化安装程序,所以,例如,它不必在每次测试运行时都解析xml配置。 – 2009-06-18 14:43:38

+0

您是否使用模拟对象来模拟单元测试中缓慢访问的对象?在工作中,我们有6000个单元测试,他们需要大约一分钟或者两个跑步。 – 2009-12-21 02:32:41

回答

1

如果没有被测试的进一步认识,这很容易展示自己的只有两种方法:

  • 使用更好的硬件(对不起)
  • 简化测试逻辑

您可能甚至希望在测试运行中运行一个分析器,并查看是否有任何特别低效的测试。

+0

我想我必须在测试用例上运行一个分析器。 – 2009-06-18 17:33:30

25

就像加速其他代码一样。了解哪些测试花费最多时间,并了解如何优化它们。

有很多操作可以很慢,如果你做了3000次,它会加起来。有时,在测试之间重复使用数据是值得的(即使你不应该在单元测试中这样做,如果这就是让测试以可接受的速度运行所需要的)。

时间您的测试。通常,他们中的90%将几乎立即执行,而最后的10%将几乎全部执行。找到那10%,看看他们在做什么。

通过探查器运行代码,并记下时间花费在哪里。猜测是浪费时间。测试跑步者正在做的是什么认为是毫无意义的。而不是猜测,找出它在做什么。然后你就会知道如何加快速度。

2

嗯,我不知道你的单元测试正在做什么,但你需要问自己为什么它需要20分钟。 从我的经验来看,经常会有很多测试在几毫秒内运行,并且只有很少的测试能够弥补剩下的所需时间。通常这些测试涉及IO /网络/ DB-Stuff。 例如,如果您由于网络延迟而花费了大量时间,则可以考虑并行运行测试。

你可以追捕这些测试并寻找改进。但是,让你的测试更快,并不会让你的实际代码更好。 您可能想要关注需要大量时间的测试,因为被测试的类不是最优的。查明和改善这些情况很可能会使您的产品更好/更快。

13

一些想法:

  • 使用模拟框架,以避免撞上数据库(或进行Web服务调用等)。
  • 如果您为大量单个测试进行相同或相似设置,请尝试在测试夹具设置中进行测试(即每次测试只进行一次,而不是每次测试一次)。
  • 我相信一些测试框架,让你在并行
+0

+1嘲笑。 – 2009-06-23 19:53:35

+1

我发现的两个最好的嘲笑框架是Mockito和JMockit。 JMockit允许模拟静态和最终方法,而Mockito对于快速模拟非常舒服。结合两部作品GREAT。 – Epaga 2009-06-24 12:48:53

+0

Upvoted for mocking ... – ABcDexter 2017-04-10 06:06:24

10

您可能希望将单元测试分成套件运行测试。你的应用程序是模块化的吗?你多久需要运行所有测试?如果您的开发人员只运行与他们自己的模块相关的单元测试,并且您每天晚上和/或在CI上运行一系列测试,那么这是可以接受的吗?是否有任何特别的单元测试是非常复杂的(我知道,我正在进入功能和集成测试,但这条线有时是模糊的),但是它们中的任何一个都可以运行于理智 - 在开发阶段,然后在CI上全面运行?

编辑:只是踢,我将简要介绍一下测试程序,在我以前的项目

首先之一,测试系统的发展是有机的,这意味着它不是原先计划但随着它的发展而被修改和改变。因此它并不完美,并且有一些命名规则随着时间的流逝变得扑朔迷离。

  • 在开发者层面,我们用一个简单的两分钟的测试套件称为签入,确认是代码是足够健康的加入树干。
  • 最重要的是,我们在CI机器上持续运行健康测试。这些是更复杂的集成和功能测试,所有单元测试和所有回归测试的简化版本。
  • 复杂的测试套件(小时数)在白天和黑夜远程运行,并在第二天早上编译结果。

自动测试 - 这是mutt的坚果。

+2

这是个不好的建议。您应该运行尽可能多的测试以确保所有组件都独立工作并且一起工作。你永远不能假设系统的一个方面是“正在工作”,因为你没有在这个领域发展。 – 2009-06-18 07:24:40

+5

“一起”的事情会受到其他一些测试的影响,比如集成测试等等。单元测试是关于单元的,所以单元应该独立工作。并不意味着我们不需要一起测试所有的东西,但这不是一个单元测试,海事组织。 – 2009-06-18 07:42:04

+1

@atc:那么您从来没有参与整个测试套件(集成测试和全部)需要几个小时的场景。 在提交之前,开发人员所需的一切都是为了确保完整性,即基本功能。这个测试不应该花费几分钟的时间,否则你会有空闲的开发人员。确保一切正常,可以在晚上完成。 – mikek 2009-06-18 07:46:47

5

开始于:
a)获取有关Junit测试运行时间的统计信息。您可能已经在测试报告中提供了信息。
b)取出前10个测试班(按时间)并尽量减少时间。这需要在不断的基础上完成。
c)尝试通过重构或甚至改变测试方法来缩短运行时间。
我遇到的一个这样的情况是在一个测试类中用于CRUD测试用例。更新测试用例首先创建了funtionlaity然后进行了更新。但是我们已经在单独测试用例中测试了创建。因此,在这些情况下,您可以链接测试像

@Test() 
    public void testCreate() throws Exception 
    {} 
    @Test(dependsOnMethods = "testCreate") 
    public void testAmend() throws Exception 
    {} 
    @Test(dependsOnMethods = "testAmend") 
    public void testDelete() throws Exception 
    {} 

所以你节省了做重复测试。 d)另外一个例子,我能够显着减少时间。 我们有一个系统(inherietd),其中每个测试用例都调用SetUp(Strating Spring Server等),并在运行后关闭系统资源。这非常耗时,所以我重构了它以在测试套装之前和整个套装之后启动Coomon资源完成后关闭这些。 e)根据你的项目,它们可能是你可能需要消除的其他瓶颈。

How to manage Build time in TDD

3

显然有什么东西在你的测试需要很长的时间。

有时候,你无法绕过慢速测试。例如,测试Spring可以读取它的所有配置文件,测试hibernate映射的工作原理。这些测试的好处在于,他们只需要在单个测试中运行,然后就可以完全嘲笑它,但是您也可以决定将它们作为集成测试的一部分来运行,并让构建服务器担心它。

试验的其余部分是缓慢的或者是因为他们正在做的IO或因为它们过于CPU约束。

IO可以是很多事情。 Web服务和数据库调用可以被抽象出来和嘲弄,如果需要的话,你需要做的几个真正的调用可以被移动到集成阶段。记录功能的确可以减慢速度 - 特别是对于3000个测试用例。我会说完全关闭日志记录,并在测试失败时依靠你的大脑和调试器。

可能存在的IO本身是单元被测试的情况。例如,如果您正在测试将表数据写入磁盘的数据库服务器的一部分。在这种情况下,尽量保持内存尽可能多。在Java中,许多IO抽象都具有内存中的实现。

CPU界的测试有不同的口味,以及。纯粹的性能和吞吐量测试应该在集成测试阶段。如果您正在旋转一堆线程来尝试审核并发错误,那么您可以将大型测试移到集成阶段,并在常规测试套件中保留一个“简单”版本。

最后,分析器是你的朋友。很可能您的代码部分可以更高效,并且可以显着提高您的测试速度。

4

我假设你已经通过所有其他通常的步骤,如嘲讽数据库调用不见了,优化测试设置相等的,什么是采取试运行这么长时间是,你有3000周的测试不是单独的测试是非常慢。

如果是这样,一种方法是多线程的测试运行。 Test-NG支持这一点。将你的测试从junit转换到test-ng并不难,只需要做一次。

测试,必须按顺序运行,可以很容易地标明:

@Test(sequential = true) 
public class ATest { 
    ... 

在一个多核心机,你会看到在运行时巨大的改进。即使在单核上,随着某些线程等待io操作,您会看到一个很好的改进。

在这里看到关于如何设置此细节:

http://beust.com/weblog/archives/000407.html

希望这有助于。

....

一些更多的建议 - 我不能没有使用持续集成相信。相信我30位开发人员不会超载您的CI服务器。即使您无法购买CI,也可以在自己的机器上安装hudson - 安装需要10分钟,而且效益非常大。每个开发人员都在等待单元测试完成或让服务器为您完成,请问您的经理哪个更糟。为打破构建的人穿上一顶笨拙的帽子通常足以说服开发人员进行单元测试。

如果checkin的质量真的是一个巨大的问题(不要忘记签入总是可以回滚),请考虑Teamcity - 它运行测试并且在测试失败时不提交代码。

最后,一个可能适合您的用例的选项也是clover and bamboo最新版本记录了哪些代码通过什么测试进行测试,以及何时进行更改,只执行相关测试。这可能非常强大。

但是请记住像test-ng,teamcity和clover这样的聪明工具只会让你变得如此好 - 好的测试不会自己写!

总结我的解决办法是尝试以下步骤全部或部分:

  1. 优化试验 - 模拟考试,常用的设置等
  2. 并行运行
  3. 测试获取别的东西来为您运行测试 - 使用哈德森或类似软件将其作为离线任务
  4. 只运行需要运行的测试 - 将它们分类到软件包或使用三叶草和竹子。
1

我同意Pablojim。平行测试。我们正在使用clearcase,并将查看器中的所有内容都转换为真正慢的东西。当我们在双核上进行并行化时,我们的测试速度提高了6-8倍。

我们正在使用CPPUnit框架,我们只是添加了一个python脚本来启动不同线程上的不同测试套件。

我们也使用clearmake来并行化构建过程。我们的下一步可能是对开发者的客户进行测试并行化。

1

将整个测试套件移动到一个连续的整合引擎系统中,这样开发人员不必每次都运行它们。这样的系统比开发者更耐心。

4

您在拨打junit时是否使用fork="yes"?如果是这样,请确保设置​​3210,否则junit任务将为每个TestCase类启动一个新的VM。通过3000个单元测试,这将会产生巨大的差异。

http://ant.apache.org/manual/Tasks/junit.html

1

我会解决这个问题,就像任何其他的性能问题:

  1. 不要对这个问题是
  2. 分析测试执行一个分析器,以确定哪些热点假设
  3. 分析一个热点,每次代码更改后重新测试。

您可能会发现,您最终必须深入研究该测试赛跑者。您可以使用类似cavaj的反编译工具从类文件生成源代码(尽管很明显,它比原始代码更难读取)。您可能会发现测试运行器实施中的某些内容会影响性能。例如,您已经提到将XML配置文件读为测试运行器执行的活动 - 这可能会影响性能。

您最终可能发现性能问题的另一个领域是在自定义“基础”测试用例类中。这些往往会增加许多便利性,但可能很难记住,在大型项目中,您的便利添加行为可能会在10k测试中摊销,无论每个测试是否需要便利行为。

1

我建议有两个版本(增量会在每次运行检查,并运行了一个完整的构建夜晚)约7分钟

增量运行较短的测试,而全力打造运行所有测试时间不到40分钟。

Clearcase确实鼓励了分支的恶梦,但是每个开发者应该可以拥有两个构建版本。我会质疑每个开发人员在自己的分支上的价值,因为我相信开发人员在同一分支上(两人或更多)协同工作会带来一些好处。

注意:一个持续集成服务器可以有任意数量的代理,如果您无法承受多台服务器,则可以使用PC作为构建代理。 (你必须至少有30个)

1

这是我会采取的方法。

  1. 回顾你的测试用例,寻找任何多余的测试。通过3000次测试,您有可能是不需要的双倍和五倍覆盖部件。
  2. 挑出你的“金丝雀”。这些是你想要经常运行的测试,这些测试会嗅到其他部分的危险。它们很可能是测试组件之间使用的公共API接口的更高级别的测试用例。如果其中一个失败,您可以进入并运行组件的完整测试套件。
  3. 开始迁移到像TestNG这样的框架并开始对测试用例进行分类,然后仅运行您正在使用夜间完整测试进行分类的类别。
0

加速大型测试套件的最有效方法是逐步运行它,以便只重新执行自上次测试运行后更改的代码更改。毕竟,最快的测试将始终是执行而不是的测试。 8 ^)

困难的部分实际上是让这个工作。我目前正在为JUnit 4进行增量测试,JUnit 4是JMockit开发人员测试工具包中“JMockit Coverage”工具的一部分。这还不成熟,但我相信它会运作良好。

0

DB访问和网络延迟可能是一个需要检查的区域。如果您在集成测试中执行大量数据库访问,则可能需要使用HSQL,H2或Derby等内存数据库,而不是像Oracle这样的“真实”数据库。如果您使用的是Hibernate,您还必须更改Hibernate配置中的设置以使用特定于该数据库的方言(例如,HSQLDialect而不是OracleDialect)。曾经参与过一个项目,每次完整构建最终都需要删除并重新创建整个Oracle架构,并通过网络执行大量数据库测试,有时需要长达20分钟,然后发现有人签入并且事件被破坏再次。 :(

理想情况下,您希望只有一个可用于这两个数据库的数据库脚本,但最终必须同步两个不同的数据库创建脚本 - 一个用于生产,一个用于集成测试。

数据库在相同的JVM vs网络数据库 - 可能会有所不同。