【问题标题】:Verify scheduledExecutorService calls the runnable multiple times using Java mockito使用 Java mockito 验证 scheduleExecutorService 多次调用可运行对象
【发布时间】:2021-06-22 16:54:48
【问题描述】:

我每分钟调用一次服务。我正在尝试编写一个单元测试来检查调度程序是否继续调用可运行对象,即使调用抛出异常也是如此。 已经有一个测试用例确认在可运行对象中捕获的异常没有冒泡。

class ServiceRunner {

@NonNull
private final Service service;

@NonNull
private final ScheduledExecutorService scheduler;

@Inject
public ServiceRunner(final Service service) {
    this(service, Executors.newSingleThreadScheduledExecutor());
}

public void run() {
    scheduler.scheduleAtFixedRate(this::runService, 0, 1, TimeUnit.MINUTES);
}

@VisibleForTesting
void runService() {
    try {
        service.run();
    } catch {
        log.info("Exception");
    }
} } 

到目前为止,我尝试创建一个模拟调度程序和一个调度程序对象。 两种方法都适用

verify(service).run();

但是

verify(service, times(2)).run();

失败并显示仅调用一次的消息。

两者都只调用一次可运行方法,然后在单元测试中终止。 我不明白为什么调度程序在测试时不工作。

请建议如何为此用例编写测试用例。

【问题讨论】:

  • 您的测试是否有睡眠等待 2 分钟才能验证?
  • 添加睡眠 2 分钟有效。能够捕获 service.run() 的多个调用。谢谢你的建议。你能解释一下为什么这里需要睡觉吗?
  • 在理想的单元测试中,您不需要验证service.run 已被scheduler 调用了多少次,因为此时您正在尝试测试scheduler 是否有效或不是。您应该测试的是,当您调用 ServiceRunner.run 时,它是否调用了调度程序上带有相关参数的相关方法。您可以通过注入调度程序的模拟/间谍实例来做到这一点。单元测试框架/库代码是当您最终进行长时间运行的无意义测试时

标签: java unit-testing mockito guice scheduledexecutorservice


【解决方案1】:

你的测试用例完全有缺陷,因为你没有足够抽象。

假设您有 2000 个单元测试,但仍然有一个错误的抽象。 2000 次测试并不多,但它可以在大约 1 分钟内运行。现在假设 1% 的测试依赖于 ServiceRunner 类,即 20 个测试。认为 20 个类使用一个名为 ServiceRunner 的类并不是没有道理的。好吧,现在您的测试不是运行大约 1 分钟,而是运行 1 分钟 + 20 * 1 分钟,所以 21 分钟。

所以,帮自己一个忙,向构造函数添加一个参数,说明服务必须运行的固定速率。当你测试它时,确保它以固定的速率运行。

这样,你可以让你的测试等待 2 * 1 秒(如果你写了 1 秒),或者更好的是 2 * 100 毫秒。

你的测试会变成这样:

var unit = MILLISECONDS;
var duration = 100;

var service = mock(Service.class);
var serviceRunner = new ServiceRunner(service, duration, unit);
serviceRunner.run();
unit.sleep(2 * duration);
verify(service, times(2)).run();

另外,不要忘记关闭你的scheduler,通常是通过换行调用它:

class ServiceRunner {
...
public void shutdown() {
  scheduler.shutdown();
}
...
}

然后调整您的测试:

var unit = MILLISECONDS;
var duration = 100;

var service = mock(Service.class);
var serviceRunner = new ServiceRunner(service, duration, unit);
serviceRunner.run();
unit.sleep(2 * duration);
serviceRunner.shutdown();
unit.sleep(duration); // Give it more time to make sure it's called twice and not three times.
verify(service, times(2)).run(); //

【讨论】:

    猜你喜欢
    • 2018-11-03
    • 2017-04-07
    • 2010-11-11
    • 2015-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多