在Java 8的java.time API中模拟时间

neu*_*242 78 java datetime mocking java-8 java-time

Joda Time有一个很好的DateTimeUtils.setCurrentMillisFixed()来模拟时间.

它在测试中非常实用.

Java 8的java.time API中是否存在等价物?

dka*_*zel 63

最接近的是Clock物体.您可以随时使用(或从系统当前时间)创建Clock对象.所有date.time对象都有重载now方法,这些方法将时钟对象替换为当前时间.因此,您可以使用依赖注入来注入具有特定时间的Clock:

public class MyBean {
    private Clock clock;  // dependency inject
    ...
    public void process(LocalDate eventDate) {
      if (eventDate.isBefore(LocalDate.now(clock)) {
        ...
      }
    }
  }
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,请参阅Clock JavaDoc

  • 是.特别是,`Clock.fixed`在测试中很有用,而`Clock.system`或`Clock.systemUTC`可能在应用程序中使用. (11认同)
  • 遗憾的是,没有可变时钟允许我将其设置为非滴答时间,但稍后修改该时间(您可以使用joda执行此操作).这对于测试时间敏感的代码很有用,例如具有基于时间的到期的缓存或将来调度事件的类. (8认同)
  • @bacar Clock是抽象类,您可以创建自己的测试时钟实现 (2认同)

Lui*_*lli 20

我使用了一个新类来隐藏Clock.fixed创建并简化测试:

public class TimeMachine {

    private static Clock clock = Clock.systemDefaultZone();
    private static ZoneId zoneId = ZoneId.systemDefault();

    public static LocalDateTime now() {
        return LocalDateTime.now(getClock());
    }

    public static void useFixedClockAt(LocalDateTime date){
        clock = Clock.fixed(date.atZone(zoneId).toInstant(), zoneId);
    }

    public static void useSystemDefaultZoneClock(){
        clock = Clock.systemDefaultZone();
    }

    private static Clock getClock() {
        return clock ;
    }
}
Run Code Online (Sandbox Code Playgroud)
public class MyClass {

    public void doSomethingWithTime() {
        LocalDateTime now = TimeMachine.now();
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)
@Test
public void test() {
    LocalDateTime twoWeeksAgo = LocalDateTime.now().minusWeeks(2);

    MyClass myClass = new MyClass();

    TimeMachine.useFixedClockAt(twoWeeksAgo);
    myClass.doSomethingWithTime();

    TimeMachine.useSystemDefaultZoneClock();
    myClass.doSomethingWithTime();

    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 如果多个测试并行运行并更改TimeMachine时钟,那么线程安全性如何呢? (4认同)

Cla*_*lke 8

我用了一个字段

private Clock clock;
Run Code Online (Sandbox Code Playgroud)

然后

LocalDate.now(clock);
Run Code Online (Sandbox Code Playgroud)

在我的生产代码中.然后我在单元测试中使用Mockito来使用Clock.fixed()模拟时钟:

@Mock
private Clock clock;
private Clock fixedClock;
Run Code Online (Sandbox Code Playgroud)

嘲讽:

fixedClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
doReturn(fixedClock.instant()).when(clock).instant();
doReturn(fixedClock.getZone()).when(clock).getZone();
Run Code Online (Sandbox Code Playgroud)

断言:

assertThat(expectedLocalDateTime, is(LocalDate.now(fixedClock)));
Run Code Online (Sandbox Code Playgroud)


Ste*_*erl 6

我发现使用Clock杂乱的生产代码.

您可以使用JMockitPowerMock在测试代​​码中模拟静态方法调用.JMockit示例:

@Test
public void testSth() {
  LocalDate today = LocalDate.of(2000, 6, 1);

  new Expectations(LocalDate.class) {{
      LocalDate.now(); result = today;
  }};

  Assert.assertEquals(LocalDate.now(), today);
}
Run Code Online (Sandbox Code Playgroud)

编辑:在阅读关于Jon Skeet对类似问题的回答的评论后,我不同意我过去的自我.最重要的是,这个论点使我确信在模拟静态方法时你不能平行化测试.

但是,如果必须处理遗留代码,则仍然可以/必须使用静态模拟.