From time to time in Java 8 java.time API

Joda Time has a nice DateTimeUtils.setCurrentMillisFixed () to simulate time.

This is very practical in tests.

Is there an equivalent in Java 8 java.time API ?

+73
java datetime java-8 mocking java-time
Jun 30 '14 at 13:29
source share
8 answers

The immediate task is the Clock object. You can create a Clock object at any time (or from the current system time). All date.time objects have overloaded now methods, which instead use the clock object instead of the current time. Thus, you can use dependency injection to inject hours 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.

+59
Jun 30 '14 at 13:57
source share

I used the new class to hide the creation of Clock.fixed and simplify the tests:

 public class TimeMachine { private static Clock clock = Clock.systemDefaultZone(); private static ZoneId zoneId = ZoneId.systemDefault(); public static LocalDateTime now() { return LocalDateTime.now(getClock()); } public static void useFixedClockAt(LocalDateTime date){ clock = Clock.fixed(date.atZone(zoneId).toInstant(), zoneId); } public static void useSystemDefaultZoneClock(){ clock = Clock.systemDefaultZone(); } private static Clock getClock() { return clock ; } } 
 public class MyClass { public void doSomethingWithTime() { LocalDateTime now = TimeMachine.now(); ... } } 
 @Test public void test() { LocalDateTime twoWeeksAgo = LocalDateTime.now().minusWeeks(2); MyClass myClass = new MyClass(); TimeMachine.useFixedClockAt(twoWeeksAgo); myClass.doSomethingWithTime(); TimeMachine.useSystemDefaultZoneClock(); myClass.doSomethingWithTime(); ... } 
+19
Mar 31 '15 at 4:16
source share

I find using Clock clutter up your production code.

You can use JMockit or PowerMock to simulate calls to static methods in your test code. JMockit example:

 @Test public void testSth() { LocalDate today = LocalDate.of(2000, 6, 1); new Expectations(LocalDate.class) {{ LocalDate.now(); result = today; }}; Assert.assertEquals(LocalDate.now(), today); } 

EDIT : After reading the comments on Jon Skeet, the answer to a similar question here on SO, I disagree with my past self. Most of all, the argument convinced me that you cannot paralyze tests when you make fun of static methods.

You can / should still use static modeling if you have to deal with legacy code.

+6
Apr 14 '15 at 11:42
source share

I used the field

 private Clock clock; 

and then

 LocalDate.now(clock); 

in my production code. Then I used Mockito in my unit tests to mock Clock using Clock.fixed ():

 @Mock private Clock clock; private Clock fixedClock; 

Mocking:

 fixedClock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); doReturn(fixedClock.instant()).when(clock).instant(); doReturn(fixedClock.getZone()).when(clock).getZone(); 

Statement:

 assertThat(expectedLocalDateTime, is(LocalDate.now(fixedClock))); 
+6
Jun 28 '17 at 7:17
source share

Here's a working way to override the current system time to a specific date for testing JUnit in a Java 8 web application using EasyMock

Joda Time is sure, good (thanks Steven, Brian, you made our world a better place), but I was not allowed to use it.

After some experimentation, I eventually came up with a way to mock time to a specific date in the Java 8 java.time API using EasyMock

  • without Joda Time API
  • and without PowerMock.

Here's what you need to do:

What you need to do in the test class

Step 1

Add the new java.time.Clock attribute to the MyService class being tested and make sure that the new attribute is correctly initialized by default with the help of the instantiation block or 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

Introduce the new clock attribute into the method that calls the current date-time. For example, in my case, I had to check if the date stored in dataase happened before LocalDateTime.now() , which I moved using LocalDateTime.now(clock) , for example:

 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 you need to do in the test class

Step 3

In the test class, create a mock clock object and enter it into the tested instance of the class just before you call the proven doExecute() method, then reset it will appear again like this:

 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 that the February 3, 2017 date was correctly entered into the MyService instance and used in the comparison instruction, and then it was correctly reset to the current date using initDefaultClock() .

+1
Aug 23 '17 at 7:21
source share

This example shows how to combine Instant and LocalTime (a detailed explanation of conversion issues )

Class under the test

 import java.time.Clock; import java.time.LocalTime; public class TimeMachine { private LocalTime from = LocalTime.MIDNIGHT; private LocalTime until = LocalTime.of(6, 0); private Clock clock = Clock.systemDefaultZone(); public boolean isInInterval() { LocalTime now = LocalTime.now(clock); return now.isAfter(from) && now.isBefore(until); } } 

A groovy test

 import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized import java.time.Clock import java.time.Instant import static java.time.ZoneOffset.UTC import static org.junit.runners.Parameterized.Parameters @RunWith(Parameterized) class TimeMachineTest { @Parameters(name = "{0} - {2}") static data() { [ ["01:22:00", true, "in interval"], ["23:59:59", false, "before"], ["06:01:00", false, "after"], ]*.toArray() } String time boolean expected TimeMachineTest(String time, boolean expected, String testName) { this.time = time this.expected = expected } @Test void test() { TimeMachine timeMachine = new TimeMachine() timeMachine.clock = Clock.fixed(Instant.parse("2010-01-01T${time}Z"), UTC) def result = timeMachine.isInInterval() assert result == expected } } 
0
Aug 12 '16 at 13:03
source share

I need an instance of LocalDate instead of LocalDateTime .
For this reason, I created the following utility class:

 public final class Clock { private static long time; private Clock() { } public static void setCurrentDate(LocalDate date) { Clock.time = date.toEpochDay(); } public static LocalDate getCurrentDate() { return LocalDate.ofEpochDay(getDateMillis()); } public static void resetDate() { Clock.time = 0; } private static long getDateMillis() { return (time == 0 ? LocalDate.now().toEpochDay() : time); } } 

And use for this as:

 class ClockDemo { public static void main(String[] args) { System.out.println(Clock.getCurrentDate()); Clock.setCurrentDate(LocalDate.of(1998, 12, 12)); System.out.println(Clock.getCurrentDate()); Clock.resetDate(); System.out.println(Clock.getCurrentDate()); } } 

Exit:

 2019-01-03 1998-12-12 2019-01-03 

Replaced all the creation of LocalDate.now() with Clock.getCurrentDate() in the project.

Because this is a spring loaded app. Before the test profile test simply set a predefined date for all tests:

 public class TestProfileConfigurer implements ApplicationListener<ApplicationPreparedEvent> { private static final LocalDate TEST_DATE_MOCK = LocalDate.of(...); @Override public void onApplicationEvent(ApplicationPreparedEvent event) { ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment(); if (environment.acceptsProfiles(Profiles.of("test"))) { Clock.setCurrentDate(TEST_DATE_MOCK); } } } 

And add to spring.factories :

org.springframework.context.ApplicationListener = com.init.TestProfileConfigurer

0
Jan 03 '19 at 19:38
source share

With PowerMockito to test spring loading, you can mock ZonedDateTime . You need the following.

Annotations

In the test class, you need to prepare a service that uses ZonedDateTime .

 @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(SpringRunner.class) @PrepareForTest({EscalationService.class}) @SpringBootTest public class TestEscalationCases { @Autowired private EscalationService escalationService; //... } 

Precedent

In the test, you can prepare the desired time and get it in response to a method call.

  @Test public void escalateOnMondayAt14() throws Exception { ZonedDateTime preparedTime = ZonedDateTime.now(); preparedTime = preparedTime.with(DayOfWeek.MONDAY); preparedTime = preparedTime.withHour(14); PowerMockito.mockStatic(ZonedDateTime.class); PowerMockito.when(ZonedDateTime.now(ArgumentMatchers.any(ZoneId.class))).thenReturn(preparedTime); // ... Assertions } 
0
Mar 18 '19 at 13:04 on
source share



All Articles