Search code examples
javajunittimermockito

Using JUnit/Mockito to confirm that java.util.Timer.schedule() does what is expected?


I've seen rather a lot of posts about time-based testing techniques in JUnit/Mockito but there seem to be so many considerations that I've become confused about how I should think of my own code under test - and how/what exactly I should test.

My code under test is as follows:

class ClassUnderTest {
    Clock clock; // Thought this might be a useful DI, but not sure how...
    String delayString;
    Timer timer;

    public ClassUnderTest(String delayString, Clock clock) {
        this.clock = clock;
        this.delayString = delayString;
        init();
    }

    private void init() {
        ChronoUnit unit = parseDelayStringToGetUnit(); // Implementation not shown here
        Integer amountOfTime = parseDelayStringToGetAmount(); // Implementation not shown here
        long delay = calculateDelay(unit, amountOfTime); // Implementation not show here
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                taskToRun();
            }
        }, delay);
    }

    private void taskToRun() {
        // Does something after a delay
        // Happy to amend signature to take params or return a value ...
    }
}

It is admittedly very cut down but I wanted to cut through the unimportant stuff. It's basic function, when instantiated, is to call taskToRun() but only after parsing another string that's passed and which can be deserialised to a ChronoUnit and associated amount. There's only likely to be a limited number of delay lengths passed in, but they could range from 15 minutes to 8 hours.

I've read that I shouldn't need to test the timer itself, but I still feel that my test method should confirm that taskToRun() does indeed run eventually but not before the desired delay has expired. I thought that passing in a java.time.Clock as a dependency injection would help time-based testing, but at the moment I cannot see how (here) and clearly it isn't used for anything (yet).

Is what I want to test correct in principle and, if so, how do I go about doing it ?

EDIT:

Apologies. Just realised that the delayString also includes 'epochStart', an instant in time from when the delay should begin. The current calculation of the primitive 'delay' would instead calculate:

instantWhenDelayExpires = epochStart + (amount of ChronoUnit)

and the timer would then schedule using the instantWhenDelayExpires rather than an amount of delay.

I don't think this particularly complicates my question though ?


Solution

  • but I still feel that my test method should confirm that taskToRun() does indeed run eventually but not before the desired delay has expired

    Indeed and in order to validate that why don't you rely on Timer.schedule(TimerTask task, long delay) that specifies the delay ?
    In this way you could add a constructor in ClassUnderTest for your test that accepts a Timer and you could mock it during the unit test. To assert that the Timer does what it is designed to do, you have just to verify that the mock was invoked with the correct parameters and especially the delay parameter.

    @Mock
    Timer mockedTimer;
    
    @Test
    void constructor(){
       long expectedDelay = ...;
       ClassUnderTest objectUnderTest = new ClassUnderTest(..., mockedTimer);
       Mockito.verify(mockedTimer).schedule(Mockito.any(),  expectedDelay);
    }