-1

I have a model constructor that generates a timestamp as this.timestamp = null == timestamp ? ZonedDateTime.now().toInstant().toEpochMilli() : timestamp;

How to mock ZonedDateTime? I need to validate using the test case that the value of the timestamp is the current time in milliseconds.

Since it is a model, injecting a dependency of a separate clock class is not possible in the constructor and I do not wish to change the source code.

Neelotpal
  • 337
  • 5
  • 17
  • 5
    1. Why not just use `Instant.now()` instead of `ZonedDateTime.now().toInstant()`? That would avoid one useless step. 2. you can't mock `ZonedDateTime.now()` (or `Instant.now()`) directly. If you want to modify or mock functionality like that you can do it using a `Clock` object and use the `now(Clock)` method of either class instead of the argument-less one. – Joachim Sauer Dec 22 '20 at 09:17
  • is your timestamp value final? Do you want to create read object and only set this single value or you want to mock the whole object? – Beri Dec 22 '20 at 09:18
  • When testing time related code, you might need to use *dependency injection* to use a mock `Clock`. You don't need a mocking framework. See https://stackoverflow.com/questions/27067049/unit-testing-a-class-with-a-java-8-clock – Raedwald Dec 22 '20 at 10:32

1 Answers1

0

It depends on your class and what you want to do:

  1. If you want to mock all properties of a class, and the class is not final, then use a mocking framework like Mockito.

    MyClass mocked = Mockito.mock(MyClass.class);       
    Mockito.when(mocked.getTimestamp()).thenReturn(**/ your value **/ )
    
  2. If your class is final, you want to only mock this single property or then property is final:

You can create a static factory, that provides you with Instant and then in your test replace the factory.

 class TimeFactory {

  private static Supplier<Instant>  INSTANT_SUPPLIER = Instant::now;
  public static Instant getInstant(){ return INSTANT_SUPPLIER.get(); }
  public static void setInstantSupplier(Supplier<Instant> new){ INSTANT_SUPPLIER= supplier; } 
} 

And in your constructor:

this.timestamp = null == timestamp ? TimeFactory.getInstant().toEpochMilli() : timestamp;

Now in your test, you can us the setter method:

 TimeFactory.setInstantSupplier(()-> /*Your value*/ )
 // create your object

Just remember to return to default supplier after the test:

TimeFactory.setInstantSupplier(Instant::now);
  1. If your property is not final, then add a package setter, that can be used in your test.

  2. Next solution (not recommended though) is to use reflection. But this is not mocking, this is using force to set a private (event final) properties.

  3. There is also a way to overwrite the clock for tests

Beri
  • 11,470
  • 4
  • 35
  • 57
  • There's already a dedicated interface for `Supplier` and it's `Clock` (granted it also provides time zone information. but those belong together anyway). – Joachim Sauer Dec 22 '20 at 10:51
  • It is nice to know, thanks :) But Supplier is more of a concept, to get the general idea, how to overwrite Suppliers. Learn how to fish as they say. – Beri Dec 22 '20 at 11:26