【发布时间】:2022-01-03 14:52:22
【问题描述】:
假设我有这个类,ClassToBeTested,它的方法调用之间的时间间隔很重要。 foo 返回不同的值,具体取决于您在过去 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"))
}
只是在文档中发现,它说:
所有可以实例化的实现都必须是最终的、不可变的和线程安全的。
因此,Clock 的可变实现似乎是不正确的。但是查看堆栈溢出的帖子,许多人建议使用可变时钟 (example)。 Some 还建议模拟 Clock 抽象类。当我尝试这样做时,jMock(这是我正在使用的)不喜欢它,因为Clock 不是一个接口。显然,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)));
让时间提前。但我认为这会破坏封装。
在这一点上,我没有想法。我能做什么?
【问题讨论】:
标签: java mocking java-time jmock