如何使用 EasyMock、没有 Joda Time 和 PowerMock 将当前系统时间覆盖到 Java 8 Web 应用程序中的特定日期?

How to override current system time to a specific date in a Java 8 web application with EasyMock, without Joda Time, and without PowerMock?

我需要模拟时间(特定 日期,这意味着我可能不会使用 anyLong())用于 Java 8 中的测试目的,仅使用 java.time,因此没有 Joda Time,在 EasyMock 下的测试环境中。

EasyMock 不允许您模拟像 LocalDateTime.now() 这样的静态方法。 PowerMock 可以,但不允许我使用 PowerMock。

Joda Time 库提供了一种巧妙而优雅的方式来模拟时间 DateTimeUtils.setCurrentMillisFixed()DateTimeUtils.setCurrentMillisSystem() 回到现在。

只有我不允许使用 Joda Time。

有人告诉我使用模拟时钟 java.time.Clock class 实例并将其注入 LocalDateTime.now(),如下所示:LocalDateTime.now(mockClock)

有没有办法在没有 Joda Time 的情况下 正确地实现它并且 没有 PowerMock?

正好有办法。这是您需要做的:

测试需要做的事情class

步骤 1

向测试的 class 添加一个新的 java.time.Clock 属性 MyService 并确保使用实例化块或构造函数将新属性正确初始化为默认值:

import java.time.Clock;
import java.time.LocalDateTime;

public class MyService {
  // (...)
  private Clock clock;
  public Clock getClock() { return clock; }
  public void setClock(Clock newClock) { clock = newClock; }

  public void initDefaultClock() {
    setClock(
      Clock.system(
        Clock.systemDefaultZone().getZone() 
        // You can just as well use
        // java.util.TimeZone.getDefault().toZoneId() instead
      )
    );
  }
  { 
    initDefaultClock(); // initialisation in an instantiation block, but 
                        // it can be done in a constructor just as well
  }
  // (...)
}

第 2 步

将新属性 clock 注入到调用当前日期时间的方法中。例如,在我的例子中,我必须检查数据库中存储的日期是否发生在 LocalDateTime.now() 之前,我将其替换为 LocalDateTime.now(clock),如下所示:

import java.time.Clock;
import java.time.LocalDateTime;

public class MyService {
  // (...)
  protected void doExecute() {
    LocalDateTime dateToBeCompared = someLogic.whichReturns().aDate().fromDB();
    while (dateToBeCompared.isBefore(LocalDateTime.now(clock))) {
      someOtherLogic();
    }
  }
  // (...) 
}

考试需要做的事情class

步骤 3

在测试 class 中,创建一个模拟时钟对象并在调用测试方法 doExecute() 之前将其注入到测试 class 的实例中,然后将其重置紧接着,像这样:

import java.time.Clock;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import org.junit.Test;

public class MyServiceTest {
  // (...)
  private int year = 2017;  // Be this a specific 
  private int month = 2;    // date we need 
  private int day = 3;      // to simulate.

  @Test
  public void doExecuteTest() throws Exception {
    // (...) EasyMock stuff like mock(..), expect(..), replay(..) and whatnot

    MyService myService = new MyService();
    Clock mockClock =
      Clock.fixed(
        LocalDateTime.of(year, month, day, 0, 0).toInstant(OffsetDateTime.now().getOffset()),
        Clock.systemDefaultZone().getZone() // or java.util.TimeZone.getDefault().toZoneId()
      );
    myService.setClock(mockClock); // set it before calling the tested method

    myService.doExecute(); // calling tested method 

    myService.initDefaultClock(); // reset the clock to default right afterwards with our own previously created method

    // (...) remaining EasyMock stuff: verify(..) and assertEquals(..)
    }
  }

在调试模式下检查,你会看到 2017 Feb 3 的日期已正确注入 myService 实例并用于比较指令,然后已正确重置为当前日期 initDefaultClock().