I want to test the method calculateApiTokenExpireDate()
. I created a spy for currentDateTime()
so that it returns static date. But it still returns the current date.
Could someone plese show me where is my mistake?
Thanks
public class ServiceHelper {
static final long DEFAULT_OUTDATED_TIME_MILLIS = 86400000;
static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm";
long oneTimeOutDatedMillis = DEFAULT_OUTDATED_TIME_MILLIS;
protected String getApiTokenExpireDate(){
DateTimeFormatter customFormat = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN);
LocalDateTime tokenExpireDate = calculateApiTokenExpireDate();
return tokenExpireDate.format(customFormat);
}
protected LocalDateTime calculateApiTokenExpireDate(){
LocalDateTime now = currentDateTime();
return now.plus(oneTimeOutDatedMillis, ChronoUnit.MILLIS);
}
protected LocalDateTime currentDateTime() {
return LocalDateTime.now();
}
}
public class ServiceHelperTest {
private ServiceHelper serviceToTest;
private ServiceHelper spy;
@BeforeEach
void beforeEach() {
serviceToTest = new ServiceHelper();
serviceToTest.oneTimeOutDatedMillis = 86400000;
spy = Mockito.spy(serviceToTest);
}
@Test
void calculateApiTokenExpireDate_return_plus_one_day_date() {
/* prepare */
doReturn(LocalDateTime.of(2023, 1, 24, 14, 1)).when(spy).currentDateTime();
/* execute */
LocalDateTime tokenExpireDate = serviceToTest.calculateApiTokenExpireDate();
/* test */
assertEquals(2023, tokenExpireDate.getYear());
assertEquals(1, tokenExpireDate.getMonthValue());
assertEquals(25, tokenExpireDate.getDayOfMonth());
}
}
CodePudding user response:
Calls to this
cannot be spied, because the this
reference is not changed. You can change your class to accept in a java.time.Clock
instance or on a function/interface which returns the current date/time. Then you can inject Clock#fixed()
in your tests.
Class:
public class ServiceHelper {
static final long DEFAULT_OUTDATED_TIME_MILLIS = 86400000;
static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm";
private final Clock clock;
long oneTimeOutDatedMillis = DEFAULT_OUTDATED_TIME_MILLIS;
public ServiceHelper(final Clock clock) {
this.clock = clock;
}
// ...
protected LocalDateTime calculateApiTokenExpireDate(){
LocalDateTime now = currentDateTime();
return now.plus(oneTimeOutDatedMillis, ChronoUnit.MILLIS);
}
protected LocalDateTime currentDateTime() {
return LocalDateTime.now(clock);
}
}
Test:
public class ServiceHelperTest {
private ServiceHelper serviceToTest;
private Instant now;
@BeforeEach
void beforeEach() {
/* prepare */
now = Instant.now(); // or: Instant.parse("...")
serviceToTest = new ServiceHelper(Clock.fixed(now, ZoneOffset.UTC));
serviceToTest.oneTimeOutDatedMillis = 86400000;
}
@Test
void calculateApiTokenExpireDate_return_plus_one_day_date() {
/* execute */
LocalDateTime tokenExpireDate = serviceToTest.calculateApiTokenExpireDate();
/* test */
// assert against this.now ...
}
}
CodePudding user response:
It looks like you are mocking the wrong instance of the class. In the test case, you are mocking the spy
instance of the ServiceHelper
class, but you are calling the method on the serviceToTest
instance, which is the original "unmocked" object. To fix this, you should call the method on the spy instance, like this:
LocalDateTime tokenExpireDate = spy.calculateApiTokenExpireDate();
Also I would recommend you to read that section from official documentation with quite good examples.