使用 Thread.sleep 进行测试
使用 Thread.sleep() 来加速测试的推荐方法是什么。
我正在测试一个具有重试功能的网络库,当连接断开或发生超时错误等时。但是,该库在重试之间使用 Thread.sleep() (因此它不会连接数千个服务器重新启动时的时间)。该调用显着减慢了单元测试速度,我想知道有哪些选项可以覆盖它。
请注意,我愿意实际更改代码,或使用模拟框架来模拟 Thread.sleep(),但想先听听您的意见/建议。
What are the recommended approaches to using Thread.sleep()
to speed up tests.
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). 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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
将与时间相关的功能委托给单独的组件通常是一个好主意。这包括获取当前时间,以及像 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(). This way it is easy to substitute this component with mock during testing, as well as switch to a different implementation.
我刚刚遇到了类似的问题,我创建了一个
Sleeper
接口来抽象这个问题:默认实现使用
Thread.sleep()
:在我的单元测试中,我注入一个 < code>FixedDateTimeAdvanceSleeper:
这允许我在单元测试中查询时间:
请注意,您需要首先使用
DateTimeUtils.setCurrentMillisFixed( new DateTime( "2014-03-26T09:37:13 " ).getMillis() );
在测试开始时使用DateTimeUtils.setCurrentMillisSystem();
测试后再次恢复时间I just faced a similar issue and I created a
Sleeper
interface to abstract this away:The default implementation uses
Thread.sleep()
:In my unit tests, I inject a
FixedDateTimeAdvanceSleeper
:This allows me to query the time in a unit test:
Note that you need to fix the time first using
DateTimeUtils.setCurrentMillisFixed( new DateTime( "2014-03-26T09:37:13" ).getMillis() );
at the start of your test and restore the time again after the test usingDateTimeUtils.setCurrentMillisSystem();
通过设置器配置睡眠时间,并提供默认值。因此,在单元测试中,使用一个小参数(例如 1)调用 setter,然后执行调用 Thread.sleep() 的方法。
另一种类似的方法是通过布尔值进行配置,这样如果
boolean
设置为false<,则根本不会调用
Thread.sleep()
/代码>。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()
.Another similar approach is to make if configurable via a boolean, so that
Thread.sleep()
isn't called at all if theboolean
is set tofalse
.创建一些代表重试延迟策略的重试延迟类型。针对延迟调用策略类型的某种方法。随意嘲笑它。没有条件逻辑或
true
/false
标志。只需注入您想要的类型即可。在ConnectRetryPolicy.java
中 在SleepConnectRetryPolicy.java
中 在MockConnectRetryPolicy.java中
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. Just inject the type that you want.In ConnectRetryPolicy.java
In SleepConnectRetryPolicy.java
In MockConnectRetryPolicy.java
我会争论你为什么要测试 Thread.sleep。看来我是你试图测试某些事件的结果的行为。
即如果发生以下情况会发生什么:
如果您基于事件对代码进行建模,那么您可以测试发生特定事件时应该发生什么,而不必提出一个屏蔽并发 API 调用的构造。否则你真正在测试什么?您是在测试应用程序对不同刺激的反应,还是只是测试 JVM 是否正常工作?
我同意其他读者的观点,有时对任何代码时间或线程相关的抽象进行抽象很有用,即虚拟时钟 http ://c2.com/cgi/wiki?VirtualClock 这样您就可以模拟任何计时/并发行为并专注于单元本身的行为。
听起来您还应该采用一种状态模式,以便您的对象根据其所处的状态具有特定的行为。即 AwaitingConnectionState、ConnectionDroppedState。转换到不同的状态将通过不同的事件,即超时、断开连接等。不确定这是否满足您的需求,但它确实删除了很多条件逻辑,这些逻辑可能使代码更加复杂和不清楚。
如果您采用这种方式,那么您仍然可以在单元级别测试行为,同时仍然可以在稍后通过集成测试或验收测试进行现场测试。
I would argue why are you trying to test Thread.sleep. It seems to be me you're trying to test the behaviour as a consequence of some event.
i.e. what happens if:
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. Else what are you really testing? Are you testing how your application reacts to different stimuli or simply testing the JVM is working correctly?
I agree with the other readers that sometimes it's useful to put an abstraction around any code time or thread related i.e. 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.
It also sounds like you should adopt a state pattern so you object has specific behaviour depending on what state it's in. i.e AwaitingConnectionState, ConnectionDroppedState. Transition to different states would be via the different events i.e. 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.
尤金是对的,制作你自己的组件来包装你无法控制的系统我自己就这样做了,我想我会分享,这被称为“SelfShunt' 检查一下:
Generator
是一个类,当您调用getId()
时,它会返回当前系统时间。我们创建测试类“SelfShunt”并成为 SystemTime 组件,这样我们就可以完全控制时间。
我们包装不受我们控制的组件。
然后创建一个接口,以便我们的测试可以“SelfShunt”
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:
Generator
is a class that when you callgetId()
it returns the current system time.We make the test class 'SelfShunt' and become the SystemTime component that way we have full control of what the time is.
We wrap the component that isn't under our control.
Then make an interface so our test can 'SelfShunt'
对于这些类型的情况,我实现了所有休眠类都实现的默认接口:
然后使用此方法而不是 Thread.sleep() 来启用测试:
现在,编写快速测试非常容易,因为您可以简单地模拟对 sleep() 的调用以立即返回(使用 Mockito 的示例):
For these kinds of cases, i implement a default interface that all sleepy classes implement:
Then use this method instead of
Thread.sleep()
to enable testing:Now, writing fast tests is easy as pie, as you can simply mock the call to sleep() to return immediately (example using Mockito):