Search code examples
javajunitmockitopowermocklocaldate

Using PowerMockRunner with LocalDate class to mock today's date


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?


Solution

  • 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());
    }