使用Thread.sleep进行测试

时间:2009-12-08 17:44:53

标签: java unit-testing testing sleep

使用Thread.sleep()加速测试的推荐方法是什么。

当连接断开或发生超时错误时,我正在测试具有重试功能的网络库。但是,库在重试之间使用Thread.sleep()(因此在服务器时它不会连接数千次)正在重新启动)。该调用显着减慢了单元测试,我想知道选项是什么来覆盖它。

注意,我愿意实际更改代码,或者使用模拟框架来模拟Thread.sleep(),但是我想先听听您的意见/建议。

6 个答案:

答案 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)

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'