简体   繁体   English

使用Thread.sleep进行测试

[英]Testing with Thread.sleep

What are the recommended approaches to using Thread.sleep() to speed up tests. 使用Thread.sleep()加速测试的推荐方法是什么?

I am testing a network library with a retry functionality when connections are dropped or timeout errors occur, etc. The library however, uses a Thread.sleep() between the retries (so it won't connect thousands times while the server is restarting). 当连接断开或发生超时错误时,我正在测试具有重试功能的网络库。但是,库在重试之间使用Thread.sleep() (因此在服务器重启时它不会连接数千次) 。 The call is slowing the unit tests significantly, and I wonder what the options are to override it. 这个调用显着减慢了单元测试,我想知道选项是什么来覆盖它。

Note, I'm open to actually changing the code, or using a mocking framework to mock Thread.sleep(), but would like to hear your opinions/recommendation first. 注意,我愿意实际更改代码,或者使用模拟框架来模拟Thread.sleep(),但是我想先听听您的意见/建议。

It is usually a good idea to delegate time-related functionality to a separate component. 将与时间相关的功能委托给单独的组件通常是个好主意。 That include getting the current time, as well as delays like Thread.sleep(). 这包括获取当前时间,以及像Thread.sleep()这样的延迟。 This way it is easy to substitute this component with mock during testing, as well as switch to a different implementation. 这样,在测试期间很容易用mock替换这个组件,以及切换到不同的实现。

I just faced a similar issue and I created a Sleeper interface to abstract this away: 我刚刚遇到了类似的问题,我创建了一个Sleeper接口来抽象它:

public interface Sleeper
{
    void sleep( long millis ) throws InterruptedException;
}

The default implementation uses Thread.sleep() : 默认实现使用Thread.sleep()

public class ThreadSleeper implements Sleeper
{
    @Override
    public void sleep( long millis ) throws InterruptedException
    {
        Thread.sleep( millis );
    }
}

In my unit tests, I inject a FixedDateTimeAdvanceSleeper : 在我的单元测试中,我注入了一个FixedDateTimeAdvanceSleeper

public class FixedDateTimeAdvanceSleeper implements Sleeper
{
    @Override
    public void sleep( long millis ) throws InterruptedException
    {
        DateTimeUtils.setCurrentMillisFixed( DateTime.now().getMillis() + millis );
    }
}

This allows me to query the time in a unit test: 这允许我查询单元测试中的时间:

assertThat( new DateTime( DateTimeUtils.currentTimeMillis() ) ).isEqualTo( new DateTime( "2014-03-27T00:00:30" ) );

Note that you need to fix the time first using DateTimeUtils.setCurrentMillisFixed( new DateTime( "2014-03-26T09:37:13" ).getMillis() ); 请注意,您需要先使用DateTimeUtils.setCurrentMillisFixed( new DateTime( "2014-03-26T09:37:13" ).getMillis() );来修复时间DateTimeUtils.setCurrentMillisFixed( new DateTime( "2014-03-26T09:37:13" ).getMillis() ); at the start of your test and restore the time again after the test using DateTimeUtils.setCurrentMillisSystem(); 在测试开始时,使用DateTimeUtils.setCurrentMillisSystem();在测试后再次恢复时间DateTimeUtils.setCurrentMillisSystem();

Make the sleeping time configurable through a setter, and provide a default value. 通过设置器配置休眠时间,并提供默认值。 So in your unit tests, call the setter with a small argument (1 for example), and then execute the method that would call Thread.sleep() . 因此在单元测试中,使用一个小参数(例如1)调用setter,然后执行调用Thread.sleep()

Another similar approach is to make if configurable via a boolean, so that Thread.sleep() isn't called at all if the boolean is set to false . 另一种类似的方法是通过布尔值进行配置,以便在boolean设置为false根本不调用Thread.sleep()

I would argue why are you trying to test Thread.sleep. 我会说你为什么要测试Thread.sleep。 It seems to be me you're trying to test the behaviour as a consequence of some event. 似乎是我,你试图测试一些事件导致的行为。

ie what happens if: 即如果出现以下情况:

  • connection timeout 连接超时
  • connection dropped 连接掉线了

If you model code based on events then you can test what should happen should a particular event occurred rather than having to come up with a construct that masks the concurrent API calls. 如果您根据事件对代码进行建模,那么您可以测试在特定事件发生时应该发生什么,而不是必须提出掩盖并发API调用的构造。 Else what are you really testing? 还有你真正测试的是什么? Are you testing how your application reacts to different stimuli or simply testing the JVM is working correctly? 您是在测试应用程序对不同刺激的反应,还是只是测试JVM是否正常工作?

I agree with the other readers that sometimes it's useful to put an abstraction around any code time or thread related ie Virtual clock http://c2.com/cgi/wiki?VirtualClock so you can mock out any timing/concurrent behaviour and concentrate on the behaviour of the unit itself. 我同意其他读者的观点,有时在任何代码时间或线程相关的情况下放置抽象是有用的,即虚拟时钟http://c2.com/cgi/wiki?VirtualClock,这样你就可以模拟任何时间/并发行为并集中精力单位本身的行为。

It also sounds like you should adopt a state pattern so you object has specific behaviour depending on what state it's in. ie AwaitingConnectionState, ConnectionDroppedState. 这听起来你应该采用状态模式,因此你的对象具有特定的行为,具体取决于它所处的状态。即AwaitingConnectionState,ConnectionDroppedState。 Transition to different states would be via the different events ie timeout, dropped connection etc. Not sure if this overkill for your needs but it certainly removes a lot of conditional logic which can make the code more complicated and unclear. 转换到不同的状态将是通过不同的事件,即超时,断开连接等。不确定这是否满足您的需求,但它肯定删除了许多条件逻辑,可以使代码更复杂和不清楚。

If you approach this way, then you can still test behaviour at the unit level whilst still testing in situ with an integration test or acceptance test later. 如果采用这种方式,那么您仍然可以在单元级别测试行为,同时仍然可以通过集成测试或验收测试进行现场测试。

Create some retry delay type that represents the policy for retry delays. 创建一些表示重试延迟策略的重试延迟类型。 Invoke some method on the policy type for the delay. 在延迟的策略类型上调用一些方法。 Mock it as you like. 随心所欲地嘲笑它。 No conditional logic, or true / false flags. 没有条件逻辑或true / false标志。 Just inject the type that you want. 只需注入您想要的类型。

In ConnectRetryPolicy.java 在ConnectRetryPolicy.java中

public interface ConnectRetryPolicy {
    void doRetryDelay();
}

In SleepConnectRetryPolicy.java 在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);
        }
    }
}

In MockConnectRetryPolicy.java 在MockConnectRetryPolicy.java中

public final class MockConnectRetryPolicy implements ConnectRetryPolicy {    
    @Override
    public void doRetryDelay() {
        // no delay
    }
}

Eugene is right, make your own component to wrap the system that is out of your control Just done this myself thought I'd share, this is known as ' SelfShunt ' check this out: Eugene是对的,制作你自己的组件来包装不受你控制的系统刚刚完成这个我以为我会分享,这被称为' SelfShunt '检查出来:

Generator is a class that when you call getId() it returns the current system time. 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;
    }
}

We make the test class 'SelfShunt' and become the SystemTime component that way we have full control of what the time is. 我们使测试类'SelfShunt'成为SystemTime组件,这样我们就可以完全控制时间。

public class BlundellSystemTime implements SystemTime {

    @Override
    public long currentTimeMillis(){
        return System.currentTimeMillis();
    }
}

We wrap the component that isn't under our control. 我们包装不受我们控制的组件。

public interface SystemTime {

    long currentTimeMillis();

}

Then make an interface so our test can 'SelfShunt' 然后创建一个界面,这样我们的测试就可以'SelfShunt'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM