测试使用ScheduledExecutorService的代码(不使用Sleep)

时间:2015-09-08 13:30:42

标签: java multithreading unit-testing junit scheduledexecutorservice

我有一个验证对象,它通过一系列检查来运行输入。如果输入未通过任何检查,则验证结束。

通过所有检查的输入根据滑动时间窗口进行分组。当第一条输入到达时,此窗口开始。所以这就是流程:

1)第一个输入到达。 2)输入通过所有检查。 3)由于没有活动计时器,因此将输入放入新的篮子中。定时器窗口开始N秒。 4)在此计时器窗口内通过所有检查的任何后续输入将被分组到同一个篮子中。 5)计时器熄灭后,将发送篮子。 6)任何进一步的有效输入将启动一个新的计时器,并重复该过程。

目前,为了确保有效输入正确组合在一起,我在单元测试中使用Thread.sleep(即一旦我发送了一些输入,我会睡几秒钟,然后唤醒并制作确定派出的篮子包含了预期的一切。

这开始变得烦人,因为我有700多个单元测试,每次运行完整套件时,这个测试集合都是瓶颈。

时间窗口只是一个ScheduledExecutorService。为了能够更快地测试此功能,我应该创建一个可设置的时间窗口对象吗?

3 个答案:

答案 0 :(得分:3)

您的“单元测试”听起来有点像集成测试。您不仅要测试使用ScheduledExecutorService的单位,还要测试ScheduledExecutorService本身。

更好的方法是注入 模拟 ScheduledExecutorService。换句话说,您不应该测试定时事件实际发生在四秒钟之后;您应该只需要测试您的设备要求调度程序在四秒钟后运行它。

这就是模拟进入的地方。你注入模拟调度程序,在你的单元上执行一些操作,使它与调度程序交互,然后你可以查询模拟以验证交互实际上是以预期的方式发生的。

如果你做得对,每个测试用例都可以用毫秒或者微秒而不是秒来完成。

答案 1 :(得分:2)

我发现DeterministicScheduler (来自jMock lib)是测试使用ScheduledExecutorService的代码的好方法。

它提供了类似的功能,例如TestScheduler提供了使用RxJava的代码或DelayController提供了使用协程的Kotlin代码。

在两种情况下,tick()advanceTimeBy()所做的与前面提到的lib完全相同: 它使虚拟时间前进并运行任何任务,这些任务有望在给定的时间范围内执行。

您需要添加核心jMock库才能使用它。

例如使用Gradle:


dependencies {
    //Used only as provider of DeterministicScheduler (test implementation of ScheduledExecutorService)
    testImplementation("org.jmock:jmock:2.12.0")
}

主题外:据我所知,它是常规的purouse功能,与jMock模拟功能无关。 理想情况下,最好将其作为单独的JAR / Maven工件提供,以便人们可以轻松地将其拉出而无需添加整个jmock lib。 我已经用这个建议创造了an issue

答案 2 :(得分:0)

使ScheduleExecutorService本身可测试非常困难。令人沮丧的是,其实现(ScheduledThreadPoolExecutor)确实具有now()方法。原则上,您可以覆盖此设置并控制时间!问题在于该方法是程序包私有的和最终的,因此不能被覆盖。可以使用PowerMock之类的方法来覆盖它。

如果您无法覆盖此方法,那么实际上ScheduleExecutorService中没有太多可以使用的方法。原则上,您可以实现自己的ThreadPoolExecutor子类,以兑现ScheduledExectuorService,但涉及实现自定义BlockingQueue,这是一件很复杂的事情。

最简单的方法是使用ScheduleExecturoService的Mock实现-有一个(不完整的)示例在Mockito Wiki上完全做到了这一点。