【问题标题】:Using PowerMockRunner with LocalDate class to mock today's date使用 PowerMockRunner 和 LocalDate 类来模拟今天的日期
【发布时间】:2019-03-07 18:20:44
【问题描述】:

考虑如下使用PowerMockRunnerDateUtilTest 类:

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

}

我想了解为什么 getPreviousWorkingDayAsDate2_whenMonday 在模拟 LocalDate.class 之后将 expected LocalDate 初始化移动到 getPreviousWorkingDayAsDate2_whenMonday 失败?

此外,可以改进此测试吗?

【问题讨论】:

    标签: java junit mockito powermock localdate


    【解决方案1】:

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

    是的 - 重构 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());
    }
    

    【讨论】:

    • Clock clock = monday.atStartOfDay().toInstant(ZoneId.systemDefault());toInstant (java.time.ZoneOffset) in ChronoLocalDateTime cannot be applied to (java.time.ZoneId)  
    • Clock clock = Clock.fixed(monday.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault()); ?那将是正确的分配? docs.oracle.com/javase/8/docs/api/java/time/…
    • 我修复了编译错误 - 你的变体也可以工作。
    【解决方案2】:

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

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

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

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

    【讨论】:

    • 感谢这个非常有用的解释:)
    【解决方案3】:

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

    DateSupplier 是一个类,您可以在创建“现在”日期的任何地方注入。 用DateSupplier.get() 替换创建日期。 测试时,您只需模拟 DateSupplier 并返回您想要的日期。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多