I need to mock time (to a specific date, meaning I may not use anyLong()
) for testing purposes in Java 8 using solely java.time
, thus without Joda Time, in a testing environment under EasyMock.
EasyMock doesn't let you mock static methods like LocalDateTime.now()
. PowerMock does, but I am not allowed to use PowerMock.
Joda Time library provides a neat and elegant way to mock time with DateTimeUtils.setCurrentMillisFixed()
, and DateTimeUtils.setCurrentMillisSystem()
to get back to the present instant.
Only I am not allowed to use Joda Time.
I was told to use a mock clock java.time.Clock
class instance and inject it into LocalDateTime.now()
like this: LocalDateTime.now(mockClock)
Is there a way to implement it properly without Joda Time and without PowerMock?
It happens there is a way. Here's what you need to do:
Add a new java.time.Clock
attribute to the tested class MyService
and make sure the new attribute will be initialized properly at default values with an instantiation block or a constructor:
import java.time.Clock;
import java.time.LocalDateTime;
public class MyService {
// (...)
private Clock clock;
public Clock getClock() { return clock; }
public void setClock(Clock newClock) { clock = newClock; }
public void initDefaultClock() {
setClock(
Clock.system(
Clock.systemDefaultZone().getZone()
// You can just as well use
// java.util.TimeZone.getDefault().toZoneId() instead
)
);
}
{
initDefaultClock(); // initialisation in an instantiation block, but
// it can be done in a constructor just as well
}
// (...)
}
Inject the new attribute clock
into the method which calls for a current date-time. For instance, in my case I had to perform a check of whether a date stored in database happened before LocalDateTime.now()
, which I replaced with LocalDateTime.now(clock)
, like so:
import java.time.Clock;
import java.time.LocalDateTime;
public class MyService {
// (...)
protected void doExecute() {
LocalDateTime dateToBeCompared = someLogic.whichReturns().aDate().fromDB();
while (dateToBeCompared.isBefore(LocalDateTime.now(clock))) {
someOtherLogic();
}
}
// (...)
}
In the test class, create a mock clock object and inject it into the tested class's instance just before you call the tested method doExecute()
, then reset it back right afterwards, like so:
import java.time.Clock;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import org.junit.Test;
public class MyServiceTest {
// (...)
private int year = 2017; // Be this a specific
private int month = 2; // date we need
private int day = 3; // to simulate.
@Test
public void doExecuteTest() throws Exception {
// (...) EasyMock stuff like mock(..), expect(..), replay(..) and whatnot
MyService myService = new MyService();
Clock mockClock =
Clock.fixed(
LocalDateTime.of(year, month, day, 0, 0).toInstant(OffsetDateTime.now().getOffset()),
Clock.systemDefaultZone().getZone() // or java.util.TimeZone.getDefault().toZoneId()
);
myService.setClock(mockClock); // set it before calling the tested method
myService.doExecute(); // calling tested method
myService.initDefaultClock(); // reset the clock to default right afterwards with our own previously created method
// (...) remaining EasyMock stuff: verify(..) and assertEquals(..)
}
}
Check it in debug mode and you will see the date of 2017 Feb 3 has been correctly injected into myService
instance and used in the comparison instruction, and then has been properly reset to current date with initDefaultClock()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With