使用Thread.sleep()
加速测试的推荐方法是什么。
当连接断开或发生超时错误时,我正在测试具有重试功能的网络库。但是,库在重试之间使用Thread.sleep()
(因此在服务器时它不会连接数千次)正在重新启动)。该调用显着减慢了单元测试,我想知道选项是什么来覆盖它。
注意,我愿意实际更改代码,或者使用模拟框架来模拟Thread.sleep(),但是我想先听听您的意见/建议。
答案 0 :(得分:13)
将与时间相关的功能委托给单独的组件通常是个好主意。这包括获取当前时间,以及像Thread.sleep()这样的延迟。这样,在测试期间很容易用mock替换这个组件,并切换到不同的实现。
答案 1 :(得分:3)
通过设置器配置休眠时间,并提供默认值。所以在你的单元测试中,用一个小参数(例如1)调用setter,然后执行调用Thread.sleep()
的方法。
另一种类似的方法是通过布尔值进行配置,以便在Thread.sleep()
设置为boolean
时根本不调用false
。
答案 2 :(得分:3)
我刚刚遇到了类似的问题,我创建了一个Sleeper
接口来抽象出来:
public interface Sleeper
{
void sleep( long millis ) throws InterruptedException;
}
默认实现使用Thread.sleep()
:
public class ThreadSleeper implements Sleeper
{
@Override
public void sleep( long millis ) throws InterruptedException
{
Thread.sleep( millis );
}
}
在我的单元测试中,我注入FixedDateTimeAdvanceSleeper
:
public class FixedDateTimeAdvanceSleeper implements Sleeper
{
@Override
public void sleep( long millis ) throws InterruptedException
{
DateTimeUtils.setCurrentMillisFixed( DateTime.now().getMillis() + millis );
}
}
这允许我在单元测试中查询时间:
assertThat( new DateTime( DateTimeUtils.currentTimeMillis() ) ).isEqualTo( new DateTime( "2014-03-27T00:00:30" ) );
请注意,您需要在测试开始时使用DateTimeUtils.setCurrentMillisFixed( new DateTime( "2014-03-26T09:37:13" ).getMillis() );
先确定时间,然后使用DateTimeUtils.setCurrentMillisSystem();
答案 3 :(得分:2)
创建一些重试延迟类型,表示重试延迟的策略。在延迟的策略类型上调用一些方法。随心所欲地嘲笑它。没有条件逻辑或true
/ false
标志。只需注入你想要的类型。
在ConnectRetryPolicy.java中
public interface ConnectRetryPolicy {
void doRetryDelay();
}
在SleepConnectRetryPolicy.java中
public class final SleepConnectRetryPolicy implements ConnectRetryPolicy {
private final int delay;
public SleepConnectRetryPolicy(final int delay) {
this.delay = delay;
}
@Override
public void doRetryDelay() {
try {
Thread.sleep(delay);
} catch (InterruptedException ie) {
log.error("connection delay sleep interrupted", ie);
}
}
}
在MockConnectRetryPolicy.java
中public final class MockConnectRetryPolicy implements ConnectRetryPolicy {
@Override
public void doRetryDelay() {
// no delay
}
}
答案 4 :(得分:2)
我会争辩你为什么要测试Thread.sleep。似乎是我,你试图测试一些事件导致的行为。
即。如果发生了什么:
如果您根据事件对代码进行建模,那么您可以测试特定事件发生时应该发生什么,而不是必须提出掩盖并发API调用的构造。还有你真正测试的是什么?您是在测试应用程序对不同刺激的反应,还是只是测试JVM是否正常工作?
我同意其他读者的观点,有时在任何代码时间或线程相关的情况下放置抽象是有用的,即虚拟时钟http://c2.com/cgi/wiki?VirtualClock,这样你就可以模拟任何时序/并发行为,并专注于单元的行为本身。
听起来你应该采用状态模式,因此你的对象具有特定的行为,具体取决于它所处的状态。即AwaitingConnectionState,ConnectionDroppedState。转换到不同的状态将是通过不同的事件,即超时,断开连接等。不确定这是否满足您的需要,但它肯定删除了许多条件逻辑,可以使代码更复杂和不清楚。
如果您采用这种方式,那么您仍然可以在单元级别测试行为,同时仍然可以通过集成测试或验收测试进行现场测试。
答案 5 :(得分:1)
Generator
是一个类,当您调用getId()
时,它会返回当前系统时间。
public class GeneratorTests implements SystemTime {
private Generator cut;
private long currentSystemTime;
@Before
public void setup(){
cut = Generator.getInstance(this);
}
@Test
public void testGetId_returnedUniqueId(){
currentSystemTime = 123;
String id = cut.getId();
assertTrue(id.equals("123"));
}
@Override
public long currentTimeMillis() {
return currentSystemTime;
}
}
我们使测试类'SelfShunt'成为SystemTime组件,这样我们可以完全控制时间。
public class BlundellSystemTime implements SystemTime {
@Override
public long currentTimeMillis(){
return System.currentTimeMillis();
}
}
我们包装不受我们控制的组件。
public interface SystemTime {
long currentTimeMillis();
}
然后创建一个界面,这样我们的测试就可以'SelfShunt'