简体   繁体   English

使用 Java mockito 验证 scheduleExecutorService 多次调用 runnable

[英]Verify scheduledExecutorService calls the runnable multiple times using Java mockito

I have a service invoked once every minute.我每分钟调用一次服务。 I am trying to write a unit test to check whether the scheduler continues calling the runnable even if of the calls throw an exception.我正在尝试编写一个单元测试来检查调度程序是否继续调用可运行对象,即使调用抛出异常也是如此。 Already have a test case that confirms exception caught in the runnable is not bubbled up.已经有一个测试用例确认在可运行文件中捕获的异常没有冒泡。

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");
    }
} } 

So far I tried creating a mock scheduler and a scheduler object.到目前为止,我尝试创建一个模拟调度器和一个调度器 object。 Both approaches work with两种方法都适用

verify(service).run();

But

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

fails with message stating it was called only once.失败并显示仅调用一次的消息。

Both just call the runnable method once and then terminates in unit test.两者都只调用一次可运行方法,然后在单元测试中终止。 I don't understand why scheduler is not working while testing.我不明白为什么调度程序在测试时不工作。

Please suggest how to write a test case for this use case.请建议如何为此用例编写测试用例。

Your test case is completely flawed because you didn't abstract enough.您的测试用例完全有缺陷,因为您没有足够抽象。

Let's imagine you have 2000 unit tests, and still have a wrong abstraction.假设您有 2000 个单元测试,但仍然有错误的抽象。 2000 tests isn't that much, but it can run in around 1 minute. 2000 次测试并不多,但它可以在大约 1 分钟内运行。 Now imagine that 1% of your tests rely on your ServiceRunner class, that's 20 tests.现在假设 1% 的测试依赖于ServiceRunner class,即 20 个测试。 It's not unreasonable to think that 20 classes use a class named ServiceRunner .认为 20 个类使用名为ServiceRunner的 class 并非没有道理。 Well, now your tests, instead of running for around 1 minute, they run for 1 minute + 20 * 1 minute, so 21 minutes.好吧,现在你的测试,而不是运行大约 1 分钟,它们运行 1 分钟 + 20 * 1 分钟,所以 21 分钟。

So do yourself a favor, and add a parameter to your constructor that say the fixed rate at which your service must run.因此,请帮自己一个忙,并为您的构造函数添加一个参数,说明您的服务必须运行的固定速率。 And when you test it, make sure that it runs every fixed rate.当你测试它时,确保它以每个固定速率运行。

This way, you could allow your test to wait for 2 * 1 second (if you wrote 1 second), or better 2 * 100 milliseconds.这样,你可以让你的测试等待 2 * 1 秒(如果你写了 1 秒),或者更好的 2 * 100 毫秒。

Your test would become this:你的测试会变成这样:

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();

Also, don't forget to shutdown your scheduler , usually by wrap-calling it:另外,不要忘记关闭你的scheduler ,通常是通过包装调用它:

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

Then adapt your test:然后调整您的测试:

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(); //

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

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