In my test case, I need test time sensitive method, in that method we\'re using java 8 class LocalDate, it is not Joda.
What can I do to change time
You can use supplier inside your class which you are testing to pass current time wherever date time is used.
public Supplier<LocalDateTime> localDateTime = () -> LocalDateTime.now();
and in the test method just override its value like :
myClassObj.localDateTime = () -> LocalDateTime.parse("2020-11-24T23:59:59.999");
You can simply pass the current date as function parameter.
fun doSmthng(now: LocalDate = LocalDate.now()) {
testAnotherService.doSmthng1(TestObj("my message!", now))
}
And in the test you can pass some specific date and assert on it. Idea is to inject the dependencies instead of creating explicitly in the function body.
In your code, replace LocalDate.now()
with LocalDate.now(clock);.
You can then pass Clock.systemDefaultZone()
for production and a fixed clock for testing.
This is an example :
First, inject the Clock
. If you are using spring boot just do a :
@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
Second, call LocalDate.now(clock)
in your code :
@Component
public class SomeClass{
@Autowired
private Clock clock;
public LocalDate someMethod(){
return LocalDate.now(clock);
}
}
Now, inside your unit test class :
// Some fixed date to make your tests
private final static LocalDate LOCAL_DATE = LocalDate.of(1989, 01, 13);
// mock your tested class
@InjectMocks
private SomeClass someClass;
//Mock your clock bean
@Mock
private Clock clock;
//field that will contain the fixed clock
private Clock fixedClock;
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
//tell your tests to return the specified LOCAL_DATE when calling LocalDate.now(clock)
fixedClock = Clock.fixed(LOCAL_DATE.atStartOfDay(ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
doReturn(fixedClock.instant()).when(clock).instant();
doReturn(fixedClock.getZone()).when(clock).getZone();
}
@Test
public void testSomeMethod(){
// call the method to test
LocalDate returnedLocalDate = someClass.someMethod();
//assert
assertEquals(LOCAL_DATE, returnedLocalDate);
}
If we need to mock static methods like now() we can use multiple alternatives like PowerMock:
@RunWith(PowerMockRunner.class)
@PrepareForTest({ LocalDateTime.class })
public class LocalDateTimeUnitTest {
@Test
public void givenLocalDateTimeMock_whenNow_thenGetFixedLocalDateTime() {
Clock clock = Clock.fixed(Instant.parse("2014-12-22T10:15:30.00Z"), ZoneId.of("UTC"));
LocalDateTime dateTime = LocalDateTime.now(clock);
mockStatic(LocalDateTime.class);
when(LocalDateTime.now()).thenReturn(dateTime);
String dateTimeExpected = "2014-12-22T10:15:30";
LocalDateTime now = LocalDateTime.now();
assertThat(now).isEqualTo(dateTimeExpected);
}
}
Or JMockit, indeed with JMockit we can use the MockUp class:
@Test
public void givenLocalDateTimeWithJMock_whenNow_thenGetFixedLocalDateTime() {
Clock clock = Clock.fixed(Instant.parse("2014-12-21T10:15:30.00Z"), ZoneId.of("UTC"));
new MockUp<LocalDateTime>() {
@Mock
public LocalDateTime now() {
return LocalDateTime.now(clock);
}
};
String dateTimeExpected = "2014-12-21T10:15:30";
LocalDateTime now = LocalDateTime.now();
assertThat(now).isEqualTo(dateTimeExpected);
}
Or the Expectations class:
@Test
public void givenLocalDateTimeWithExpectations_whenNow_thenGetFixedLocalDateTime() {
Clock clock = Clock.fixed(Instant.parse("2014-12-23T10:15:30.00Z"), ZoneId.of("UTC"));
LocalDateTime dateTimeExpected = LocalDateTime.now(clock);
new Expectations(LocalDateTime.class) {
{
LocalDateTime.now();
result = dateTimeExpected;
}
};
LocalDateTime now = LocalDateTime.now();
assertThat(now).isEqualTo(dateTimeExpected);
}
We can find more examples here.
Another simple alternative is to use the now() method with a fixed Clock instance. Certainly, most of the classes in java.time package have a now() method with a Clock parameter:
@Test
public void givenFixedClock_whenNow_thenGetFixedLocalDateTime() {
Clock clock = Clock.fixed(Instant.parse("2014-12-22T10:15:30.00Z"), ZoneId.of("UTC"));
String dateTimeExpected = "2014-12-22T10:15:30";
LocalDateTime dateTime = LocalDateTime.now(clock);
assertThat(dateTime).isEqualTo(dateTimeExpected);
}
Using Spring:
ClockConfiguration class:
@Configuration
public class ClockConfiguration {
private final static LocalDate LOCAL_DATE = LocalDate.of(2019, 12, 17);
@Bean
@ConditionalOnMissingBean
Clock getSystemDefaultZoneClock() {
return Clock.systemDefaultZone();
}
@Bean
@Profile("test")
@Primary
Clock getFixedClock() {
return Clock.fixed(LOCAL_DATE.atStartOfDay(ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
}
}
SomeService class:
@Service
@RequiredArgsConstructor
public class SomeService {
private final Clock clock;
public void someMethod(){
...
LocalDateTime.now(clock)
LocalDate.now(clock)
...
}
}
You must have an active "test" profile in the test:
SomeServiceTest class:
@ActiveProfiles("test")
@EnableConfigurationProperties
@SpringBootTest(classes = [YourAppMainClass])
class SomeServiceTest {
...
}
You can refactor you code to make it test-friendly, for example, replace all invocations of LocalDate.now()
with invocation of some method of custom mockable non-static class.
Alternatively, you can use PowerMock's mockStatic.