在Eclipse中设置JUnit超时时间

16

问题

在使用Eclipse运行我们所有的JUnit测试时,我能否设置默认超时时间?

背景

我的经理坚持编写单元测试,有时需要长达5分钟才能完成。当我尝试运行我们的整个测试套件(大约300个测试)时,可能需要超过30分钟。我想要实现一个机制,停止任何运行时间超过10秒的测试。

我知道可以对单个测试进行注释:

@Test(timeout=10000)

但这样做会导致他的长测试用例总是失败。我希望当他在自己的机器上运行这些测试时它们能够正常工作(如果我必须在检入代码之前对项目进行小的调整,那是可以接受的。但是删除40个不同测试文件中的超时设置就不现实了)。

我也知道我可以创建一个ant任务来为所有测试设置默认超时,类似于:

<junit timeout="10000">
  ...
</junit>

我们通常在Eclipse中通过右键单击 > 运行为 > JUnit 测试来运行测试的问题是什么。

摘要

那么,是否有一种相对轻松的方法可以为所有测试设置超时,例如使用运行配置设置、项目设置、JUnit首选项、环境变量或其他方式?我甚至可以安装其他插件,让我右键单击特定的测试文件夹并以某种其他方式运行所有测试,比如通过 ant 或其他什么...


1
需要花费5分钟的测试?听起来它们要么是在每个测试中做了太多的工作,要么就是集成测试而不是单元测试... - Donal Fellows
5
你可以将集成测试与 JUnit 测试分开。长时间运行的测试应在类似 Hudson 的构建服务器上运行,而不是在你的本地计算机上运行。 - Christian Kuetbach
@Donal:不幸的是,它们是单元测试。我不喜欢它们。我不同意它们。然而,我无能为力。因此我的问题...我的目标是至少在我的框中解决这个问题。我想写一个方面来注释所有测试方法,并使用@test(timeout=10)。然后,我只需永远不将该方面检入Subversion。这是我的计划,我想... - gMale
1
面向方面的方法不起作用,因为这些方法已经有了@Test注释,并且目前在AspectJ中没有机制来替换注释...不过我认为这个功能正在开发中。 - gMale
@Test(timeout=10) 表示超时时间为 10 毫秒 而不是 10 秒 - Stephan
@Stephan 谢谢。已更新。 - gMale
7个回答

11
可能的解决方案:将所有测试类都从另一个类扩展:TestBase 例如
TestBase中添加全局超时时间。该超时时间将应用于所有继承的类:
public class TestBase {
    @Rule
    public Timeout globalTimeout = new Timeout(10000);
}

2
这是一个很好的建议。在其他情况下肯定可以使用。但在这种情况下,这将需要更改他所有的测试,而这并不是一个选项。 - gMale
不错的技巧,但是否有任何方法可以覆盖特定测试(例如仅一个)扩展该类的值? - рüффп
当然可以。您可以在测试类中的特定方法上使用@Test(timeout=10) - alaster

1
如果您想将测试配置为最多运行十秒钟,可以尝试以下方法:
@Test(timeout=10000)

2
谢谢,但这个解决方案对原问题描述的情况不适用:“这样做会导致他的长时间测试始终失败。我希望他在自己的电脑上运行时能够正常工作……在提交之前从40个不同的测试文件中删除超时是不切实际的。” - gMale

1
我的经理坚持编写单元测试,有时需要长达5分钟才能完成。
这几乎肯定表明这些测试实际上并不是单元测试。解决这个问题的方法是:尝试重构你的测试套件,以提供相同的测试覆盖率,而无需运行那么长时间的测试用例。

1
很遗憾,它们是单元测试。他验证了一个低级函数在失败5分钟后超时。:(那个函数只有3行代码。 - gMale
6
使用时间超时需要一个时钟。实际代码将使用系统时钟,但是不必在单元测试中使用。将时钟抽象成一个对象,并使用依赖注入来消除对系统时钟的直接依赖。也就是说,不要直接调用系统时钟函数,而是引入一个 Clock 接口(或抽象基类),传递给时间超时代码。对于单元测试,使用一个模拟时间超时的 MockClock (它是一个 Clock)。对于实际的集成代码,使用一个调用系统时钟函数的 SystemClock (它也是一个 Clock)。 - Raedwald
虽然他没有使用时钟,但我理解你的观点。在这种情况下,超时实际上是基于一个失败的库调用(最终在I/O期间超时)。如果他模拟了那个调用就好了,但他没有。我不能真正改变他所有的测试,只能在我认为合适的地方使用模拟。所以我正在寻找一种方法来为所有测试设置超时。现在,我们又回到使用ant了,所以我可以使用junit任务中内置的超时,就像我在原始帖子中提到的那样。Ant很棒。 - gMale

1
也许使用启用了“慢速测试警告”的Infinitest与过滤功能的组合会做到这一点。您可以识别超出时间限制的测试并将它们添加到过滤列表中,这只会影响Eclipse内部的测试。通过可能的构建脚本通过CLI / CI等运行测试不会受到任何影响。 您可以在此处找到有关设置此功能的更多信息:http://improvingworks.com/products/infinitest/infinitest-user-guide/

这看起来很棒!几乎完全符合我所寻找的,并且是我可以在其他情况下经常使用的东西。我会试用一下,如果有效,我就会采用它... - gMale

0

几乎可以确定你的老板的测试是系统测试,假装是单元测试。如果它们应该是单元测试,但只是很慢,那么它们应该重构为使用模拟对象(mocks),以便更快地运行。

无论如何,比起面对老板并解决这个问题,更实际和委婉的方法可能是自己尝试运行速度较快的测试。我曾经在一个项目中看到过一种解决慢测试问题的方法,即所有缓慢的测试名称都包含“SytemTest”。然后在构建文件中创建了两个ant目标:一个运行所有的测试,另一个按类名过滤出SytemTest。要实现这一点,你只需重新命名一些测试,并编写自己的ant目标。


0

听起来测试套件会对你有所帮助。

你可以有两个测试套件;QuickTestsAllTests。在AllTests套件中包含QuickTests,以及需要很长时间的测试。然后所有其他测试都将进入快速测试套件。

从eclipse中,您可以一次运行整个测试套件。因此,您将运行QuickTests,这样所有其他缓慢的测试都不会运行。

或者查看此问题,了解如何对套件应用超时,该超时将应用于套件中的嵌套套件和类。与我上面的建议相结合,可以实现类似于您想要的功能。


-2

我知道这并不是真正回答你的问题,但简单的答案是不要这样做!

有条件地设置超时是错误的,因为那样你就会在自己的机器上拥有一些总是失败的单元测试。单元测试的目的是能够快速查看是否有任何破坏。必须检查失败的测试列表以确保只是长时间运行的测试,这将使一些漏洞通过。

正如一些评论者所提到的,你应该将测试分成运行快速的单元测试和运行较慢的集成测试,即为你的代码创建一个名为src/main/java的源文件夹,一个名为src/test/java的单元测试文件夹和一个名为src/integration-test/java的长时间运行测试文件夹。


谢谢您抽出时间回答,但您是对已经信奉的人讲道;) 我完全同意您的观点,但我无法做出这样的改变。因此,我的问题更多地是关于中和糟糕单元测试的影响。 - gMale
@gmale:我们都曾经历过这种情况,但如果你不继续向老板施压,什么也不会改变。我快速查看了Ant源代码,他们应用的超时并不是JUnit的事情。你可能想要使用@Ignore来忽略你不想运行的特定测试,而不是任意的超时时间。你可以编辑JUnit源代码来设置默认超时时间。你需要更改使用的JUnit库。 - brain
这更适合作为注释。 - byxor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接