I have below code in one of my methods
ZonedDateTime current = Instant.now().atZone(ZoneId.of(AMERICA_NEW_YORK));
I want to mock current
in the JUnit test.
I tried with java.time.Clock
but for this, I need to add it into the class constructor as my code is written into old versions of Spring and using XML based configuration this class cause issue because it requires constructor argument in the application-context.xml file if I use a constructor with Clock
.
Is there any way to avoid constructor configuration and mock current
in the above code.
Update
As per Pavel Smirnov's comments, I tried below but current
still returning today's date but not the one which I am mocking.
ZonedDateTime exactOneDay = ZonedDateTime.parse("Sun Oct 21 12:30:00 EDT 2018", Parser);
doReturn(exactOneDay).when(spyEmployeeHelper).getCurrentTime();
employee = getEmployees().get(0);
assertEquals(Integer.valueOf(1), employee.getNoticePeriod());
Mockito based solution where code uses plain Instant.now()
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import java.time.Clock;
import java.time.Instant;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
public class MockInstantTest {
private MockedStatic<Clock> clockMock;
@BeforeEach
public void setup() {
mockInstant(1640000000); // set desired return value 2021-12-20T11:33:20Z
}
@AfterEach
public void destroy() {
clockMock.close();
}
private void mockInstant(long expected) {
Clock spyClock = spy(Clock.class);
clockMock = mockStatic(Clock.class);
clockMock.when(Clock::systemUTC).thenReturn(spyClock);
when(spyClock.instant()).thenReturn(Instant.ofEpochSecond(expected));
}
@Test
void testWithMockedIstant() {
// invoking Instant.now() will always return the same value
assertThat(Instant.now().toString()).isEqualTo("2021-12-20T11:33:20Z");
}
}
Solution Explained
Solution relays on the fact that Instant.now()
invokes Clock.systemUTC().instant()
Clock
is abstract so we spy for non static methodsclock.instant()
is mocked to return the desired valueClock.systemUTC()
is static so we need mockStatic@Before
/@After
is required to close MockedStatic (alternatively you can use try(MockedStatic<Clock> clockMock = mockStatic(Clock.class)) {...}
)You can declare a function that returns ZoneDateTime
:
public ZoneDateTime getCurrentTime () {
return Instant.now().atZone(ZoneId.of(AMERICA_NEW_YORK));
}
and assign the result of that funciton to the current
field:
ZonedDateTime current = getCurrentTime();
Now you can simply replace it with desired value using Mockito framework:
doReturn(yourValue).when(yourObject).getCurrentTime();
When using Mockito you can easily mock like so:
ZoneId zoneId = ZoneId.of("America/New_York");
ZonedDateTime current = ZonedDateTime.now(zoneId);
Timestamp timestamp = Timestamp.from(Instant.now());
when(timestamp.toInstant()).thenReturn(Instant.from(current));
Adding Test for timeout example:
@Test
public void testForTimeout() throws InterruptedException {
ZoneId zoneId = ZoneId.of("America/New_York");
ZonedDateTime current = ZonedDateTime.now(zoneId);
Timestamp timestampBeforeCall = Timestamp.from(Instant.now());
// Call Class.method() or here instead we just introduce an artificial wait time :
Thread.sleep(3000);
Timestamp timestampAfterCall = Timestamp.from(Instant.now());
long timeoutInMilliseconds = 2000;
long diff = timestampAfterCall.getTime() - timestampBeforeCall.getTime();
log.info(String.valueOf(diff));
if(diff > timeoutInMilliseconds) {
log.error("Call Timed Out!");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With