如何用java.time.Clock模拟时间的流逝?
How do I simulate the passage of time with java.time.Clock?
假设我有这个 class、ClassToBeTested
,其中方法调用之间经过的时间很重要。 foo
returns 一个不同的值,具体取决于您在过去 x 小时内是否打过电话 foo
。
我的实现方式是在调用foo
的时候获取当前的Instant
,和上次调用foo
的Instant
进行比较,我存储在一个字段中。
由于ClassToBeTested
取决于当前时间,所以我添加了一个Clock
字段,调用者在创建ClassToBeTested
时需要传入:
class ClassToBeTested {
private final Clock clock;
private Instant lastCalled;
public ClassToBeTested(Clock clock) {
this.clock = clock;
}
public String foo() {
if (lastCalled == null || Duration.between(lastCalled, Instant.now(clock)).compareTo(Duration.ofHours(1)) >= 0) {
lastCalled = Instant.now(clock);
return "A";
} else {
lastCalled = Instant.now(clock);
return "B";
}
}
}
不过,当我编写测试时,我意识到没有创建可变 Clock
的工厂方法。我尝试创建自己的可变时钟,以便我的测试看起来像这样:
private final FakeClock fakeClock = new FakeClock();
@Test
public void fooReturnsAAfterAnHour() {
var myInstance = new ClassToTest(fakeClock);
assertThat(myInstance.foo(), is("A"));
fakeClock.goForward1Hour(); // this mutates the clock, and changes what instant() will return
assertThat(myInstance.foo(), is("A"))
}
@Test
public void fooReturnsBWithinAnHour() {
var myInstance = new ClassToTest(fakeClock);
assertThat(myInstance.foo(), is("A"));
fakeClock.goForward30Minutes(); // this mutates the clock, and changes what instant() will return
assertThat(myInstance.foo(), is("B"))
}
才发现在文档中,它说:
All implementations that can be instantiated must be final, immutable and thread-safe.
所以 Clock
的可变实现似乎是不正确的。但是查看有关堆栈溢出的帖子,许多人建议使用可变时钟(example). Some also suggest mocking the Clock
abstract class. When I tried that out, jMock (which is what I'm using) doesn't like it because Clock
is not an interface. Apparently, if I want to mock classes, I'd have to include another dependency,它主要用于模拟遗留代码。对我来说,这听起来不适合模拟 Clock
。
我还可以为 ClassToBeTested
中的 clock
字段添加一个 setter,这样我就可以:
myInstance.setClock(Clock.offset(clock, Duration.ofHours(1)));
提前时间。但我认为这会破坏封装。
在这一点上,我没有主意了。我能做什么?
您的 FakeClock
方法很好。这就是我会使用的方法。
如果您更喜欢使用库中的实现而不是您自己的实现,则可以使用 MutableClock
from the threeten-extra 库。 class 正是为这个用例设计的。
“不可变”要求来自 Clock
class 中的 removed Java 17。它被删除不是因为对 [=12= 进行了更改] 的实施,但因为该要求是有害的限制(请参阅您的用例)而没有达到有用的目的。因此,如果您使用 Java <17,我会说忽略该要求是安全的。
假设我有这个 class、ClassToBeTested
,其中方法调用之间经过的时间很重要。 foo
returns 一个不同的值,具体取决于您在过去 x 小时内是否打过电话 foo
。
我的实现方式是在调用foo
的时候获取当前的Instant
,和上次调用foo
的Instant
进行比较,我存储在一个字段中。
由于ClassToBeTested
取决于当前时间,所以我添加了一个Clock
字段,调用者在创建ClassToBeTested
时需要传入:
class ClassToBeTested {
private final Clock clock;
private Instant lastCalled;
public ClassToBeTested(Clock clock) {
this.clock = clock;
}
public String foo() {
if (lastCalled == null || Duration.between(lastCalled, Instant.now(clock)).compareTo(Duration.ofHours(1)) >= 0) {
lastCalled = Instant.now(clock);
return "A";
} else {
lastCalled = Instant.now(clock);
return "B";
}
}
}
不过,当我编写测试时,我意识到没有创建可变 Clock
的工厂方法。我尝试创建自己的可变时钟,以便我的测试看起来像这样:
private final FakeClock fakeClock = new FakeClock();
@Test
public void fooReturnsAAfterAnHour() {
var myInstance = new ClassToTest(fakeClock);
assertThat(myInstance.foo(), is("A"));
fakeClock.goForward1Hour(); // this mutates the clock, and changes what instant() will return
assertThat(myInstance.foo(), is("A"))
}
@Test
public void fooReturnsBWithinAnHour() {
var myInstance = new ClassToTest(fakeClock);
assertThat(myInstance.foo(), is("A"));
fakeClock.goForward30Minutes(); // this mutates the clock, and changes what instant() will return
assertThat(myInstance.foo(), is("B"))
}
才发现在文档中,它说:
All implementations that can be instantiated must be final, immutable and thread-safe.
所以 Clock
的可变实现似乎是不正确的。但是查看有关堆栈溢出的帖子,许多人建议使用可变时钟(example). Some also suggest mocking the Clock
abstract class. When I tried that out, jMock (which is what I'm using) doesn't like it because Clock
is not an interface. Apparently, if I want to mock classes, I'd have to include another dependency,它主要用于模拟遗留代码。对我来说,这听起来不适合模拟 Clock
。
我还可以为 ClassToBeTested
中的 clock
字段添加一个 setter,这样我就可以:
myInstance.setClock(Clock.offset(clock, Duration.ofHours(1)));
提前时间。但我认为这会破坏封装。
在这一点上,我没有主意了。我能做什么?
您的 FakeClock
方法很好。这就是我会使用的方法。
如果您更喜欢使用库中的实现而不是您自己的实现,则可以使用 MutableClock
from the threeten-extra 库。 class 正是为这个用例设计的。
“不可变”要求来自 Clock
class 中的 removed Java 17。它被删除不是因为对 [=12= 进行了更改] 的实施,但因为该要求是有害的限制(请参阅您的用例)而没有达到有用的目的。因此,如果您使用 Java <17,我会说忽略该要求是安全的。