Mocking time in Java 8's java.time API

JavaDatetimeMockingJava 8Java Time

Java Problem Overview


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

It's very practical in tests.

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

Java Solutions


Solution 1 - Java

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

Solution 2 - Java

I used a new class to hide the Clock.fixed creation 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();

    ...
}

Solution 3 - Java

I used a field

private Clock clock;

and then

LocalDate.now(clock);

in my production code. Then I used Mockito in my unit tests to mock the 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();

Assertion:

assertThat(expectedLocalDateTime, is(LocalDate.now(fixedClock)));

Solution 4 - Java

I find using Clock clutters your production code.

You can use JMockit or PowerMock to mock static method invocations in your test code. Example with JMockit:

@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's answer to a similar question here on SO I disagree with my past self. More than anything else the argument convinced me that you cannot parallize tests when you mock static methods.

You can/must still use static mocking if you have to deal with legacy code, though.

Solution 5 - Java

A bit late, but here it is what I use to mock time using the java.date API in Kotlin:

val now = LocalDate.of(2021, Month.FEBRUARY, 19)
val clock = Clock.fixed(Instant.ofEpochSecond(
    now.atStartOfDay().toEpochSecond(ZoneOffset.UTC)
), ZoneId.systemDefault())

and then you can pass your clock to the class to test

val classToTest = MyClass(clock)

Of course, inside your testable class you will use the clock to retrieve dates or times:

class MyClass(private val clock: Clock = Clock.systemDefaultZone()) {
    // ...
    fun doSomething() = LocalDate.now(clock)...

Solution 6 - Java

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

Joda Time is sure nice (thank you Stephen, Brian, you've made our world a better place) but I wasn't allowed to use it.

After some experimenting, I eventually came up with a way to mock time to a specific date in Java 8's java.time API with EasyMock

  • Without Joda Time API
  • Without PowerMock.

Here's what needs to be done:

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

Solution 7 - Java

I need LocalDate instance instead of LocalDateTime.
With such reason I created 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 usage for it is like:

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

Output:

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

Replaced all creation LocalDate.now() to Clock.getCurrentDate() in project.

Because it is spring boot application. Before test profile execution just 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

Solution 8 - Java

I used java.time.Clock with mockito dependency

testImplementation("org.mockito:mockito-core")
testImplementation("org.mockito:mockito-inline")

The service class uses a Clock field which will be mocked on the test.

@Service
public class TimeTestWithDateService {
    private final Clock clock = Clock.systemUTC();

    public TimeTest plan(UUID orderId) {
        return TimeTest.builder()
                .id(UUID.randomUUID())
                .orderId(orderId)
                .createdAt(ZonedDateTime.now(clock))
                .plannedAt(ZonedDateTime.now(clock)
                        .plusDays(1)
                        .withHour(8)
                        .truncatedTo(ChronoUnit.HOURS))
                .build();
    }

    public TimeTest ship(TimeTest timeTest) {
        return TimeTest.builder()
                .id(timeTest.getId())
                .orderId(timeTest.getOrderId())
                .createdAt(timeTest.getCreatedAt())
                .shippedAt(ZonedDateTime.now(clock))
                .build();
    }
}

@Value
@Builder
public class TimeTest {
    private UUID id;
    private UUID orderId;
    private ZonedDateTime createdAt;
    private ZonedDateTime plannedAt;
    private ZonedDateTime shippedAt;
}

The unit test uses the Mockito.mockStatic to mock the Clock.

@SpringBootTest
public class TimeTestWithDateServiceTest {
    @Autowired
    private TimeTestWithDateService timeTestService;

    private static Clock clock;
    private static ZonedDateTime now;

    @BeforeAll
    static void setupClock() {
        clock = Clock.fixed(
                Instant.parse("2020-12-01T10:05:23.653Z"),
                ZoneId.of("Europe/Prague"));
        now = ZonedDateTime.now(clock);

        var clockMock = Mockito.mockStatic(Clock.class);
        clockMock.when(Clock::systemUTC).thenReturn(clock);
    }

    @Test
    void timeTest_is_planned() {
        var orderId = UUID.randomUUID();
        var timeTest = timeTestService.plan(orderId);

        var tomorrowAt8am = now.plusDays(1).withHour(8).truncatedTo(ChronoUnit.HOURS);

        assertAll(
                () -> assertThat(timeTest).isNotNull(),
                () -> assertThat(timeTest.getId()).isNotNull(),
                () -> assertThat(timeTest.getOrderId()).isEqualTo(orderId),
                () -> assertThat(timeTest.getCreatedAt()).isEqualTo(now),
                () -> assertThat(timeTest.getPlannedAt()).isEqualTo(tomorrowAt8am),
                () -> assertThat(timeTest.getShippedAt()).isNull()
        );
    }

    @Test
    void timeTest_is_shipped() {
        var timeTest = timeTestService.plan(UUID.randomUUID());
        var shipped = timeTestService.ship(timeTest);
        assertAll(
                () -> assertThat(shipped).isNotNull(),
                () -> assertThat(shipped.getId()).isEqualTo(timeTest.getId()),
                () -> assertThat(shipped.getOrderId()).isEqualTo(timeTest.getOrderId()),
                () -> assertThat(shipped.getCreatedAt()).isEqualTo(timeTest.getCreatedAt()),
                () -> assertThat(shipped.getShippedAt()).isEqualTo(now)
        );
    }
}

Solution 9 - Java

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

A class under 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
    }

}

Solution 10 - Java

With the help of PowerMockito for a spring boot test you can mock the ZonedDateTime. You need the following.

Annotations

On the test class you need to prepare the service which uses the the ZonedDateTime.

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

Test case

In the test you can prepare a desired time, and get it in response of the 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 
}

Solution 11 - Java

Use jmockit:

Code:

// Mocking time as 9am
final String mockTime = "09:00:00"
new MockUp<LocalTime>() {
       @Mock
       public LocalTime now() {
           return LocalTime.parse(mockTime);
       }
};

Imports:

import mockit.MockUp;
import mockit.Mock;

Dependency:

<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.41</version>

Solution 12 - Java

here is the answer : https://gabstory.com/70?category=933660

import com.nhaarman.mockitokotlin2.given
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Mock
import org.mockito.Mockito.mockStatic
import org.mockito.junit.jupiter.MockitoExtension
import org.springframework.data.projection.ProjectionFactory
import org.springframework.data.projection.SpelAwareProxyProjectionFactory
import java.time.Clock
import java.time.ZonedDateTime

@ExtendWith(MockitoExtension::class)
class MyTest {
   private val clock = Clock.fixed(ZonedDateTime.parse("2021-10-25T00:00:00.000+09:00[Asia/Seoul]").toInstant(), SEOUL_ZONE_ID)
   
    @BeforeEach
    fun setup() {
        runCatching {
            val clockMock = mockStatic(Clock::class.java)
            clockMock.`when`<Clock>(Clock::systemDefaultZone).thenReturn(clock)
        }
    }
    
    @Test
    fun today(){
      assertEquals("2021-10-25T00:00+09:00[Asia/Seoul]", ZonedDateTime.now().toString())
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionneu242View Question on Stackoverflow
Solution 1 - JavadkatzelView Answer on Stackoverflow
Solution 2 - JavaLuizSignorelliView Answer on Stackoverflow
Solution 3 - JavaClaas WilkeView Answer on Stackoverflow
Solution 4 - JavaStefan HaberlView Answer on Stackoverflow
Solution 5 - JavaAlex FacciorussoView Answer on Stackoverflow
Solution 6 - JavaKiriSakowView Answer on Stackoverflow
Solution 7 - Javacatch23View Answer on Stackoverflow
Solution 8 - JavaKnoblauchView Answer on Stackoverflow
Solution 9 - JavabanterCZView Answer on Stackoverflow
Solution 10 - JavapasqualeView Answer on Stackoverflow
Solution 11 - JavaArunView Answer on Stackoverflow
Solution 12 - JavakokojustinView Answer on Stackoverflow