Mocking Time in Java 8's Java.Time API

Mocking time in Java 8's java.time API

The closest thing is the Clock object. You can create a Clock object using any time you want (or from the System current time). All date.time objects have overloaded now methods that take a clock object instead for the current time. So you can use dependency injection to inject a Clock with a specific time:

public class MyBean {
private Clock clock; // dependency inject
...
public void process(LocalDate eventDate) {
if (eventDate.isBefore(LocalDate.now(clock)) {
...
}
}
}

See Clock JavaDoc for more details

Unit testing a class with a Java 8 Clock

I don't want to add a Clock argument to the method because the real code shouldn't be concerned with passing in a clock.

No... but you might want to consider it as a constructor parameter. Basically you're saying that your class needs a clock with which to work... so that's a dependency. Treat it as you would any other dependency, and inject it either in a constructor or via a method. (I personally favour constructor injection, but YMMV.)

As soon as you stop thinking of it as something you can easily construct yourself, and start thinking of it as "just another dependency" then you can use familiar techniques. (I'm assuming you're comfortable with dependency injection in general, admittedly.)

How do I simulate the passage of time with java.time.Clock?

Your FakeClock approach is fine. That's the approach I'd use.

If you prefer to use an implementation from a library instead of your own, you could use MutableClock from the threeten-extra library. That class was designed for exactly this use case.

The "immutable" requirement was removed from the Clock class in Java 17. It was removed not because of changes being made to Clock's implementation, but because the requirement was harmfully restrictive (see your use case) while not serving a useful purpose. So if you're using Java <17, I'd say it's safe to simply ignore that requirement.

How to override current system time to a specific date in a Java 8 web application with EasyMock, without Joda Time, and without PowerMock?

It happens there is a way. Here's what you need to do:

What needs to be done in the tested class

Step 1

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
}
// (...)
}

Step 2

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

What needs to be done in the test class

Step 3

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

Writing and testing convenience methods using Java 8 Date/Time classes

In your test (and only during test!) set the clock that your convenience class uses so that you can predict the desired/expected results independently of the computer clock:

public class ConvenienceTest {

@Test
public void testGetLocalHour() {
Convenience underTest = new Convenience();

ZoneId zone = ZoneId.of("America/Los_Angeles");

ZonedDateTime fixedZdt = ZonedDateTime.now(zone).withHour(0);
underTest.setClock(Clock.fixed(fixedZdt.toInstant(), zone));
assertEquals("24", underTest.getLocalHour24HourClock());

fixedZdt = fixedZdt.withHour(1);
underTest.setClock(Clock.fixed(fixedZdt.toInstant(), zone));
assertEquals("01", underTest.getLocalHour24HourClock());

fixedZdt = fixedZdt.withHour(23);
underTest.setClock(Clock.fixed(fixedZdt.toInstant(), zone));
assertEquals("23", underTest.getLocalHour24HourClock());

// TODO test with other time zones
}

}

For this to work it of course requires that your convenience class can accept a Clock and uses it:

public class Convenience {

private Clock clock = Clock.systemDefaultZone();

/** For testing only. Sets the clock from which to get the time. */
void setClock(Clock clockForTest) {
this.clock = clockForTest;
}

public String getLocalHour24HourClock() {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("kk");
ZonedDateTime zdt = ZonedDateTime.now(clock);
return zdt.format(fmt);
}

}

With this implementation the tests just passed on my computer.

Stepping a step back: if your convenience methods are but a thin layer on top of java.time, you may start to consider how much value a unit test has. If it is doing some real work (like formatting in the above example, something that regularly goes wrong), a test is valuable, but if a method just returns a value from a single call into java.time, you may not need a test. You shouldn’t test java.time, preferably only your own code.

How to mock LocalDateTime.now() in java 8

You should use Mockito#mockStatic for this use case.

You can use it like this

try(MockedStatic<LocalDateTime> mock = Mockito.mockStatic(LocalDateTime.class, Mockito.CALLS_REAL_METHODS)) {
doReturn(LocalDateTime.of(2030,01,01,22,22,22)).when(mock).now();
// Put the execution of the test inside of the try, otherwise it won't work
}

Notice the usage of Mockito.CALLS_REAL_METHODS which will guarantee that whenever LocalDateTime is invoked with another method, it will execute the real method of the class.

How can I mock java.time.LocalDate.now()

In your code, replace LocalDate.now() with LocalDate.now(clock);.

You can then pass Clock.systemDefaultZone() for production and a fixed clock for testing.


This is an example :

First, inject the Clock. If you are using spring boot just do a :

@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}

Second, call LocalDate.now(clock) in your code :

@Component
public class SomeClass{

@Autowired
private Clock clock;

public LocalDate someMethod(){
return LocalDate.now(clock);
}
}

Now, inside your unit test class :

// Some fixed date to make your tests
private final static LocalDate LOCAL_DATE = LocalDate.of(1989, 01, 13);

// mock your tested class
@InjectMocks
private SomeClass someClass;

//Mock your clock bean
@Mock
private Clock clock;

//field that will contain the fixed clock
private Clock fixedClock;

@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);

//tell your tests to return the specified LOCAL_DATE when calling LocalDate.now(clock)
fixedClock = Clock.fixed(LOCAL_DATE.atStartOfDay(ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
doReturn(fixedClock.instant()).when(clock).instant();
doReturn(fixedClock.getZone()).when(clock).getZone();
}

@Test
public void testSomeMethod(){
// call the method to test
LocalDate returnedLocalDate = someClass.someMethod();

//assert
assertEquals(LOCAL_DATE, returnedLocalDate);
}

How to mock current date time for passing a quality gate pass in java

Tests should be independent when and where they are executed. To make them independent on the current enviroment time you should consider creating an interface that provides the current time.

e.g.

public interface TimeProvider {
ZonedDateTime getCurrentZonedDateTime()
}

for production code you have a simple implementatation that returns the current time, and for tests you can mock its behaviour e.g. using Mockito.

var timeProvider = Mockito.mock(TimeProvicer.class)
var testTime = ...
Mockito.when(timeprovider.getCurrentZonedDateTime()).thenReturn( testTime )
myProgram.setTimeProvider(timeProvider)

now the test will always return the same results.

Maybe you need to make sure that you do not call ZonedDateTime.now() (or any other method returning the current time) beside your default TimeProvider implementation.



Related Topics



Leave a reply



Submit