繁体   English   中英

你如何加速java单元测试?

[英]How do you speed up java unit tests?

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

以同样的方式加速任何其他代码。 找出哪些测试花费最多时间,并了解如何优化它们。

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

测试时间。 通常,它们中的90%几乎会立即执行,而最后10%将几乎所有时间都会执行。 找到那些10%,看看他们在做什么。

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

一些想法:

  • 使用模拟框架来避免命中数据库(或进行Web服务调用等)。
  • 如果您正在进行大量单独测试的相同或类似设置,请尝试在测试夹具设置中进行(即每个夹具完成一次而不是每次测试一次)。
  • 我相信一些测试框架允许您并行运行测试

您可能希望将单元测试拆分为套件。 您的应用程序是否模块化? 你经常需要经常进行所有测试吗? 如果您的开发人员只运行与他们自己的模块相关的单元测试,并且您每晚都运行一系列测试和/或CI,那么这是可以接受的吗?

是否有任何特定的单元测试非常复杂(我知道,我在这里正在进行功能和集成测试,但这条线有时是模糊的),但是它们中的任何一个都可以在开发过程中以健全级别运行,然后是在CI上全力以赴?

编辑 :只是为了踢,我将在之前的一个项目中简要描述测试程序

首先,测试系统的增长是有机的,这意味着它最初没有计划好,但随着它的发展而被修改和改变。 因此它并不完美,并且有一些命名惯例随着时间的推移而变得杜松。

  • 在开发人员级别,我们使用了一个名为CheckIn的简单的两分钟测试套件,它验证了代码是否足够健康以加入主干。
  • 最重要的是,我们在CI机器上不断进行健全性测试。 这些是更复杂的集成和功能测试的简化版本,所有单元测试和所有回归测试。
  • 复杂的测试套件(以小时为单位)在白天和晚上远程运行,结果在第二天早上编译。

自动测试 - 这是笨蛋的坚果。

首先:
a)获取有关Junit测试运行时间的统计信息。您可能已经在测试报告中限制了这些信息。
b)取出前10个测试课程(及时),并尝试减少时间。这需要持续进行。
c)尝试通过重构甚至改变测试方法来减少运行时间。
我遇到的一个这样的案例是在CRUD测试用例的一个Test类中。更新测试用例首先创建funtionlaity然后更新。但是我们已经测试了在seprate测试用例中创建。所以在这些情况下你可以链接你的测试用例如

@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)根据您的项目,他们可能是您可能需要解决的其他瓶颈。

如何在TDD中管理构建时间

我假设您已经完成了所有其他常用步骤,例如模拟数据库调用,优化测试设置阶段等等。测试运行这么长时间,您需要进行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分钟,而且收益很大。 问问你的经理哪个开发人员等待单元测试完成或让服务器为你做这个更糟糕。 对于破坏构建的人来说,戴上一顶愚蠢的帽子通常足以说服开发人员进行他们的单元测试。

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

最后,可能适合您的用例的选项还有三叶草和竹子 最新版本记录了通过什么测试测试的代码,并且在进行更改时,它仅运行相关测试。 这可能非常强大。

但是记住像test-ng,teamcity和clover这样的聪明工具只会让你到目前为止 - 好的测试不会自己写!

总结一下我的解决方案是尝试以下全部或部分步骤:

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

你在junit电话中使用fork="yes"吗? 如果是这样,请确保设置forkMode="once" ,否则junit任务将为每个TestCase类启动一个新VM。 通过3000次单元测试,这将产生巨大的差异。

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

显然,你的测试中有些东西需要很长时间。

有时,你无法绕过慢速测试。 例如,测试Spring可以读取所有配置文件,测试hibernate映射的工作原理,那种东西。 这些测试的好处是它们只需要在单个测试中运行然后你可以全部模拟它,但你也可以决定在集成测试中运行它们,让构建服务器担心它。

其余的测试都很慢,因为它们正在执行IO,或者因为它们过度受CPU限制。

IO可以是很多东西。 Web服务和数据库调用可以被抽象出来并进行模拟,如果需要,您可以将几个真正的调用移动到集成阶段。 记录也可以减慢速度 - 特别是对于3000个测试用例。 我会说完全关闭日志记录并在测试失败时依赖你的大脑和调试器。

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

CPU边界测试也有不同的风格。 纯性能和吞吐量测试应该在集成测试阶段。 如果您正在开发一堆线程来尝试审查并发错误,那么您可以将大测试移至集成阶段并在常规测试套件中保留“轻量级”版本。

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

如果不进一步了解正在测试的内容,那么只有两种方法可以轻松呈现:

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

您可能希望甚至在测试运行中运行探查器,看看是否存在任何特别低效的测试。

好吧,我不知道你的单元测试在做什么,但你需要问问自己为什么需要20分钟。 根据我的经验,通常会有很多测试在几毫秒内完成,很少有测试可以弥补剩余的所需时间。 通常这些是涉及IO /网络/ DB-Stuff的测试。 例如,如果由于网络延迟而花费大量时间等待,则可以考虑并行运行测试。

您可以搜索这些测试并寻找改进。 但是,让您的测试更快,并不能使您的实际代码更好。 您可能希望查找需要大量时间的测试,因为测试中的类不是最佳的。 精确定位和改善这些情况很可能会使您的产品更好/更快。

我同意Pablojim。 并行化您的测试。 我们使用clearcase并从视图服务器转移所有内容,这确实会减慢速度。 当我们在duelcore上进行并行化时,我们的测试运行速度提高了6-8倍。

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

我们还使用clearmake来并行化构建过程。 我们的下一步可能是在开发人员的客户端上并行化测试。

在Continuous INtegration引擎系统中移动完整的测试套件,因此开发人员不必每次都运行它们。 这样的系统比开发人员有更多的耐心。

我会像解决任何其他性能问题一样解决这个问题:

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

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

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

我建议有两个版本(每次签入时都会运行Incremental,以及一个晚上运行的完整版本)

增量运行在大约7分钟内运行更短的测试,而完整版本在不到40分钟内运行所有测试更长时间。

Clearcase确实鼓励分支噩梦,但每个开发人员应该能够拥有两个版本。 我会质疑在他们自己的分支上进行每次开发的价值,因为我相信让开发人员在同一分支上一起工作(成对或更多)会有一些好处。

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

这是我要采取的方法。

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

加速大型测试套件的最有效方法是以递增方式运行它,以便只重新执行自上次测试运行以来触摸代码更改的测试。 毕竟,最快的测试将始终是那些执行的测试。 8 ^)

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

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

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

数据库在同一个JVM与网络中的数据库之间 - 可能会有所不同。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM