使用 PowerMockRunner 和 LocalDate class 模拟今天的日期

Using PowerMockRunner with LocalDate class to mock today's date

考虑如下使用 PowerMockRunner:

DateUtilTest class
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);
    }

}

我想了解为什么 @Test ==> getPreviousWorkingDayAsDate2_whenMondayexpected LocalDate 初始化移动到 LocalDate.class 的模拟之后失败?

此外,这个测试是否可以改进?

另外,这个测试可以改进吗?

是 - 重构 DateUtil 以能够使用特定 Clock。例如:

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 ...;
   }
}

然后单元测试不需要任何模拟。例如:

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

这很简单。当您将预期的 LocalDate 初始化移动到 LocalDate.class 的模拟之后,那么 class 已经在全局范围内被模拟,就像在对 class 的每次调用中一样。这意味着它不会为您提供真正的 java 实现。这意味着您无法获得 LocalDate.of(2017, 10, 13) 的真正 java 实现,因为此时您用模拟版本覆盖了 class。

测试可以改进吗?是的,不要使用 powermock。

如果可以避免,使用 powermock 被认为是不好的做法。这是一个很长的故事,我不确定这是解释这一切的地方。它的简短版本:如果你不使用 power mock,你将不得不让你的代码更灵活。 Powermock 允许您作弊,而不是使您的代码灵活。当您使用 power mock 时,您在测试和生产代码中不会得到相同的行为。

阅读此处了解更多信息: Why not PowerMock

日期在测试时总是很痛苦。但是使用 DateSupplier 可以很容易地修复它。

A DateSupplier 是一个 class 你可以在任何有 'now' 创建日期的地方注入。 用 DateSupplier.get() 替换日期创建。 测试时,您只需模拟 DateSupplier 和 return 您想要的日期。

可以在此处找到详细示例:https://dzone.com/articles/mocking-jodatimes-datetime-and。 (你真的不需要番石榴的供应商)

在这种情况下:在您的 DateUtil class 中注入 DateSupplier,并在测试期间将其替换为模拟!