使用Thread.sleep进行测试
使用Thread.sleep()
加速测试的推荐方法是什么?
当连接断开或发生超时错误时,我正在测试具有重试function的网络库。但是,库在重试之间使用Thread.sleep()
(因此在服务器重启时它不会连接数千次) 。 该调用显着减慢了unit testing,我想知道选项是什么来覆盖它。
注意,我愿意实际更改代码,或者使用模拟框架来模拟Thread.sleep(),但是我想先听听您的意见/建议。
将与时间相关的function委托给单独的组件通常是个好主意。 这包括获取当前时间,以及像Thread.sleep()这样的延迟。 这样,在测试期间很容易用mock替换这个组件,以及切换到不同的实现。
通过设置器配置hibernate时间,并提供默认值。 所以在你的unit testing中,用一个小参数(例如1)调用setter,然后执行调用Thread.sleep()
。
另一种类似的方法是,如果可以通过布尔值进行配置,那么如果boolean
设置为false
则根本不调用Thread.sleep()
。
我刚刚遇到了类似的问题,我创建了一个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 ); } }
在我的unit testing中,我注入了一个FixedDateTimeAdvanceSleeper
:
public class FixedDateTimeAdvanceSleeper implements Sleeper { @Override public void sleep( long millis ) throws InterruptedException { DateTimeUtils.setCurrentMillisFixed( DateTime.now().getMillis() + millis ); } }
这允许我查询unit testing中的时间:
assertThat( new DateTime( DateTimeUtils.currentTimeMillis() ) ).isEqualTo( new DateTime( "2014-03-27T00:00:30" ) );
请注意,您需要首先使用DateTimeUtils.setCurrentMillisFixed( new DateTime( "2014-03-26T09:37:13" ).getMillis() );
来修复时间DateTimeUtils.setCurrentMillisFixed( new DateTime( "2014-03-26T09:37:13" ).getMillis() );
在测试开始时,使用DateTimeUtils.setCurrentMillisSystem();
在测试后再次恢复时间DateTimeUtils.setCurrentMillisSystem();
创建一些表示重试延迟策略的重试延迟类型。 在延迟的策略类型上调用一些方法。 随心所欲地嘲笑它。 没有条件逻辑或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 } }
我会说你为什么要测试Thread.sleep。 似乎是我,你试图测试一些事件导致的行为。
即如果出现以下情况:
- 连接超时
- 连接掉线了
如果您基于事件对代码进行建模,那么您可以测试在特定事件发生时应该发生什么,而不是必须提出掩盖并发API调用的构造。 还有你真正测试的是什么? 您是在测试应用程序对不同刺激的反应,还是只是测试JVM是否正常工作?
我同意其他读者的观点,有时在任何代码时间或线程相关的情况下放置抽象是有用的,即虚拟时钟http://c2.com/cgi/wiki?VirtualClock,这样你就可以模拟任何时间/并发行为并专注于单位本身的行为。
听起来你应该采用状态模式,因此你的对象具有特定的行为,具体取决于它所处的状态。即AwaitingConnectionState,ConnectionDroppedState。 转换到不同的状态将是通过不同的事件,即超时,断开连接等。不确定这是否满足您的需求,但它肯定删除了许多条件逻辑,可以使代码更复杂和不清楚。
如果采用这种方式,那么您仍然可以在单元级别测试行为,同时仍然可以通过集成测试或验收测试进行现场测试。
Eugene是对的,制作你自己的组件来包装你无法控制的系统我自己认为我已经分享了这个,这被称为’ SelfShunt ‘检查出来:
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’