Reputation: 16575
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?
Upvotes: 117
Views: 135155
Reputation: 7563
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)
);
}
}
Upvotes: 1
Reputation: 200
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())
}
}
Upvotes: 0
Reputation: 915
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>
Upvotes: 0
Reputation: 2408
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)...
Upvotes: 2
Reputation: 1207
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
Here's what needs to be done:
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
// (...)
}
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();
}
}
// (...)
}
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()
.
Upvotes: 2
Reputation: 18582
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
Upvotes: 1
Reputation: 10539
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.
Upvotes: 8
Reputation: 509
With the help of PowerMockito for a spring boot test you can mock the ZonedDateTime
.
You need the following.
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;
//...
}
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
}
Upvotes: 0
Reputation: 1411
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();
...
}
Upvotes: 31
Reputation: 1991
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)));
Upvotes: 21
Reputation: 1869
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
}
}
Upvotes: 0
Reputation: 31648
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
Upvotes: 87