Mocking time in Java 8's java.time API

98,534

Solution 1

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

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

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

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

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)...
Share:
98,534

Related videos on Youtube

Dee Choksi
Author by

Dee Choksi

System developer #SOreadytohelp

Updated on April 02, 2022

Comments

  • Dee Choksi
    Dee Choksi about 2 years

    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?

  • Matt Johnson-Pint
    Matt Johnson-Pint almost 10 years
    Yes. In particular, Clock.fixed is useful in testing, while Clock.system or Clock.systemUTC might be used in the application.
  • bacar
    bacar over 8 years
    Shame that there isn't a mutable clock that allows me to set it to non-ticking time, but modify that time later (you can do this with joda). This would be useful for testing time-sensitive code, e.g. a cache with time-based expiries or a class that schedules events in the future.
  • Bjarne Boström
    Bjarne Boström over 8 years
    @bacar Clock is abstract class, you could create your own test Clock implementation
  • bacar
    bacar over 8 years
    I believe that's what we ended up doing.
  • youri
    youri over 7 years
    What about thread-safety if several tests are run in parallel and change the TimeMachine clock?
  • deamon
    deamon over 6 years
    You have to pass the clock to the tested object and use it when calling time related methods. And you could remove the getClock() method and use the field directly. This method add nothing but a few lines of code.
  • Emanuele
    Emanuele over 6 years
    Banktime or TimeMachine?
  • David Groomes
    David Groomes about 6 years
    +1 for "using static mocking for legacy code" comment. So for new code, be encouraged to lean into Dependency Injection and inject a Clock (fixed Clock for tests, system Clock for production runtime).
  • Geoff Langenderfer
    Geoff Langenderfer over 2 years
    what do you think about implementing without passing Clock? Any costs/benefits? What's the benefit of passing the clock? For example, if it's a time-unrelated class
  • Alex Facciorusso
    Alex Facciorusso over 2 years
    So, first thing first: you pass Clock only if you actually need it. What I mean is if you are not getting any kind of date/time in your class you don't need to pass Clock, of course. Given that, usually the goal is trying to have single-responsibility classes, so only the classes that use an implementation based on dates will have that dependency. That said, the main advantage I believe is having a common contract and practice to follow for every class that needs a date/time.