简体   繁体   中英

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. 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 tests isn't that much, but it can run in around 1 minute. Now imagine that 1% of your tests rely on your ServiceRunner class, that's 20 tests. It's not unreasonable to think that 20 classes use a class named ServiceRunner . Well, now your tests, instead of running for around 1 minute, they run for 1 minute + 20 * 1 minute, so 21 minutes.

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.

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:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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