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