在进行模拟调用之前自动更新时间戳的最佳方法是什么?
这是我试图测试的一些虚拟代码:
public class ThingWithATimestamp {
public Long timestamp;
public String name;
public ThingWithATimestamp(String name) {
this.name = name;
}
}
public class TheClassThatDoesStuff {
private ThingConnector connector;
public TheClassThatDoesStuff(ThingConnector connector) {
this.connector = connector;
}
public void updateTheThing(MyThingWithATimestamp thing) {
thing.timestamp = currentTimestamp();
connector.update(thing);
}
}
这是我想要测试的:
public class TheClassThatDoesStuffTests {
@Test
public void canUpdateTheThing() {
ThingConnector connector = mock(ThingConnector.class);
TheClassThatDoesStuff doer = new ThisClassThatDoesStuff(connector);
doer.updateTheThing(new ThingWithATimestamp("the name"));
verify(connector, times(1)).update(SomeMatcherThatICantFigureOut);
}
我知道这段代码非常愚蠢,但我认为它准确地描绘了我想要验证的内容.我基本上需要一个匹配器来填写测试来验证时间戳是否在当前时间的X之内,所以我知道它已正确更新,并且使用适当的时间戳为对象调用了connector.update.
解决方法:
我发现处理时间关键代码最强大的方法是将所有时间关键函数包含在自己的类中.我通常称之为TimeHelper.所以这个类可能如下所示.
import java.util.Date;
public class TimeHelper{
public long currentTimeMillis(){
return System.currentTimeMillis();
}
public Date makeDate(){
return new Date();
}
}
它可能有更多相同类型的方法.现在,任何使用这些函数的类都应该有(至少)两个构造函数 – 您将在应用程序中使用的正常构造函数,以及一个包含TimeHelper作为参数的包私有构造函数.需要将此TimeHelper存储起来以备将来使用.
public class ClassThatDoesStuff {
private ThingConnector connector;
private TimeHelper timeHelper;
public ClassThatDoesStuff(ThingConnector connector) {
this(connector, new TimeHelper());
}
ClassThatDoesStuff(ThingConnector connector, TimeHelper timeHelper) {
this.connector = connector;
this.timeHelper = timeHelper;
}
}
现在,在您的类中,不要编写System.currentTimeMillis(),而是编写timeHelper.currentTimeMillis().当然,这将具有完全相同的效果;除了现在,你的班级神奇地变得更加可测试.
当你测试你的课程时,模拟一下TimeHelper.配置此模拟(使用Mockito的when和thenReturn,或者使用doReturn)返回您喜欢的任何时间值 – 无论您需要什么样的测试.如果您要在测试过程中多次调用currentTimeMillis(),您甚至可以在此处返回多个值.
现在使用第二个构造函数来创建您要测试的对象,并传入模拟.这使您可以完美控制测试中使用的时间值;并且您可以使您的断言或验证声明已经使用了正确的值.
public class ClassThatDoesStuffTest{
@Mock private TimeHelper mockTime;
@Mock private ThingConnector mockConnector;
private ClassThatDoesStuff toTest;
@Test
public void doesSomething(){
// Arrange
initMocks(this);
when(mockTime.currentTimeMillis()).thenReturn(1000L, 2000L, 5000L);
toTest = new ClassThatDoesStuff(mockConnector, mockTime);
// Act
toTest.doSomething();
// Assert
// ... ???
}
}
如果这样做,您就知道您的测试将始终有效,并且永远不会依赖于操作系统的时间切片策略.您还可以验证时间戳的确切值,而不是断言它们落在某个近似间隔内.