Consider a DateUtilTest
class as follows that uses PowerMockRunner
:
import com.reporting.utils.DateUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.time.LocalDate;
import java.util.Date;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
@PrepareForTest(DateUtil.class)
@RunWith(PowerMockRunner.class)
public class DateUtilTest {
@Test
public void getPreviousWorkingDayAsDate_whenMonday() {
//given
LocalDate date = LocalDate.of(2017, 10, 16);
LocalDate expected = LocalDate.of(2017, 10, 13);
mockStatic(LocalDate.class);
when(LocalDate.now()).thenReturn(date);
//when
Date previousWorkingDay = DateUtil.getPreviousWorkingDayAsDate();
//then
assertEquals(DateUtil.getDateFromLocalDate(expected), previousWorkingDay);
}
@Test
public void getPreviousWorkingDayAsDate2_whenMonday() {
//given
LocalDate date = LocalDate.of(2017, 10, 16);
mockStatic(LocalDate.class);
when(LocalDate.now()).thenReturn(date);
//when
Date previousWorkingDay = DateUtil.getPreviousWorkingDayAsDate();
LocalDate expected = LocalDate.of(2017, 10, 13);
//then
assertEquals(DateUtil.getDateFromLocalDate(expected), previousWorkingDay);
}
}
I want to understand why @Test
==> getPreviousWorkingDayAsDate2_whenMonday
fails when I move the expected
LocalDate
initialization to after the mocking of the LocalDate.class
?
Further, could this tests be improved?
Further, could this tests be improved?
Yes - refactor DateUtil to be able to use a specific Clock
. For example:
public class DateUtil {
private static Clock clock = Clock.systemDefaultZone();
public static setClock(Clock clock) {
assertNotProduction(); // optionally check for an environment/system variable to throw exception if used in production
DateUtil.clock = clock;
}
public static Date getPreviousWorkingDayAsDate() {
LocalDate today = LocalDate.now(clock); // use clock
...
return ...;
}
}
Then the unit test does not need any mocking. For example:
@Test
public void getPreviousWorkingDayAsDate_whenMonday() {
//given
LocalDate monday = LocalDate.of(2017, 10, 16);
Clock clock = Clock.fixed(monday.atStartOfDay(ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
DateUtil.setClock(clock);
LocalDate lastFriday = LocalDate.of(2017, 10, 13);
//when
Date previousWorkingDay = DateUtil.getPreviousWorkingDayAsDate();
//then
assertEquals(DateUtil.getDateFromLocalDate(lastFriday), previousWorkingDay);
}
@After
public void resetClock() {
DateUtil.setClock(Clock.systemDefaultZone());
}