java 如何在单元测试中避免 Thread.sleep?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36283334/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to avoid Thread.sleep in Unit tests?
提问by Rufi
Let's imagine I have the following method which should be tested:
假设我有以下应该测试的方法:
@Autowired
private RoutingService routingservice;
public void methodToBeTested() {
Object objectToRoute = initializeObjectToRoute();
if (someConditions) {
routingService.routeInOneWay(objectToRoute);
} else {
routingService.routeInAnotherWay(objectToRoute);
}
}
In this case RoutingService
is running in the separate thread, thus in it's constructor we have the following:
在这种情况下RoutingService
是在单独的线程中运行,因此在它的构造函数中我们有以下内容:
Thread thread = new Thread(this);
thread.setDaemon(true);
thread.start();
The problem is that RoutingService
changes the state of objectToRoute
and this is exactly what I want to check, but this doesn't happen straight away thus the test fails. However, if I add Thread.sleep()
then it works, but this is bad practice as I know.
问题是RoutingService
改变了状态,objectToRoute
这正是我想要检查的,但这不会立即发生,因此测试失败。但是,如果我添加Thread.sleep()
它就可以工作,但据我所知,这是不好的做法。
How can I avoid Thread.sleep()
in this case?
Thread.sleep()
在这种情况下我该如何避免?
采纳答案by forgivenson
If you are testing [Unit Test] for the method methodToBeTested
, you should simply mock routingservice
.
You shouldn't be testing any methods that methodToBeTested
calls.
如果您正在测试该方法的[单元测试] methodToBeTested
,您应该简单地模拟routingservice
。
您不应该测试任何methodToBeTested
调用的方法。
However, it sounds like you want to test the RoutingService
(you said "The problem is that RoutingService
changes the state of objectToRoute
and this is exactly what I want to check").
但是,听起来您想测试RoutingService
(您说“问题是RoutingService
更改了状态,objectToRoute
而这正是我想要检查的”)。
To test RoutingService
methods, you should write separate unit tests for those methods.
要测试RoutingService
方法,您应该为这些方法编写单独的单元测试。
回答by sarah
I suggest awaitilityfor synchronizing asynchronous tests. For example, assume you have an result object that get set after some thread operations and you want to test it. You write statement like this:
我建议等待同步异步测试。例如,假设您有一个在一些线程操作后设置的结果对象,并且您想测试它。你写这样的语句:
await()
.atMost(100, TimeUnit.SECONDS)
.untilAsserted(() -> assertNotNull(resultObject.getResult()));
It waits maximum 100 seconds, oruntil the assertion is satisfied. For instance, if getResult() returns something that is not null in between 0-100 seconds, the execution continues unlike Thread.sleep which holds the execution for given time no matter result is there or not.
它最多等待 100 秒,或直到断言得到满足。例如,如果 getResult() 在 0 到 100 秒之间返回不为空的内容,则执行会继续,这与 Thread.sleep 不同,它会在给定时间内保持执行,无论结果是否存在。
回答by fgb
You could mock objectToRoute
to set the value of a CompletableFuture
and then call get
on that in your assertion. This will wait until the value is set before continuing. Then set a timeout @Test(timeout=5000)
in case the value is never set.
您可以模拟objectToRoute
设置 a 的值,CompletableFuture
然后get
在断言中调用它。这将等到值被设置后再继续。然后设置超时@Test(timeout=5000)
以防从未设置该值。
This has the advantage that the test won't wait longer than necessary and it's harder to fail because of too short a time because you can make the timeout much larger than normal.
这样做的优点是测试不会等待超过必要的时间,并且由于时间太短而更难失败,因为您可以使超时比正常情况大得多。
回答by Serge Ballesta
It depends. As Ben Green said in his comment, sleep is dangerous in tests because it can hide a race condition. Youknow if the overall design contains such a race condition, where the service could be used before the route is ready. It it does, you should fix it in code, for example by testing a readycondition, and test it the same in your test class.
这取决于。正如本格林在他的评论中所说,睡眠在测试中是危险的,因为它可以隐藏竞争条件。您知道整体设计是否包含这样的竞争条件,即可以在路线准备好之前使用服务。确实如此,您应该在代码中修复它,例如通过测试就绪条件,并在您的测试类中对其进行相同的测试。
If you know that it cannot happen, you should document it in you main code andin your test class. That would then be a perfect justification for a sleep.
如果你知道它不会发生,你应该在你的主代码和你的测试类中记录它。这将是一个完美的睡眠理由。
(I assume that this is for an integration test - for unit tests mocksshould be enough as you were said in other answers)
(我假设这是用于集成测试 - 对于单元测试模拟应该足够了,正如您在其他答案中所说的那样)
回答by manuelvigarcia
Use Object.wait()
使用 Object.wait()
We have worked with some asynchronous processes that obtain files from directories or .ZIP archives. We use the content of the files somewhere else while the asynchronous one keeps on reading.
我们已经使用了一些从目录或 .ZIP 档案中获取文件的异步进程。我们在其他地方使用文件的内容,而异步文件继续读取。
The way we came around to test them was to wait()
on the communication object --in our case it was a Queue--, so the asynchronous process would queue.notifyAll()
whenever there was a new file ready to be used and kept working. On the other end the consumer would process one item at a time until the queue was empty and then queue.wait()
to wait for more. We do use a special object passed through the queue to indicate that there are no more items to process.
我们来测试它们的方法是wait()
在通信对象上——在我们的例子中它是一个队列——,所以queue.notifyAll()
只要有一个新文件准备好使用并保持工作,异步进程就会执行。另一方面,消费者将一次处理一个项目,直到队列为空,然后queue.wait()
等待更多。我们确实使用了一个通过队列的特殊对象来指示没有更多的项目要处理。
In your case I suppose the object you want to check, objectToRoute,
is not really created inside the method you want to test. You could wait()
on it inside the test, and notifyAll()
on it inside the method you want to test, methodToBeTested.
I know this would introduce the extra line of code in your production code base, but with nobody waiting on it, it should result harmless. It would end up something like:
在您的情况下,我想您要检查的对象objectToRoute,
并不是在您要测试的方法中真正创建的。您可以wait()
在测试中notifyAll()
使用它,在您要测试的方法中使用它,methodToBeTested.
我知道这会在您的生产代码库中引入额外的代码行,但是没有人等待它,它应该会产生无害的结果。它最终会是这样的:
public void methodToBeTested(Object objectToRoute) {
if (someConditions) {
routingService.routeInOneWay(objectToRoute);
} else {
routingService.routeInAnotherWay(objectToRoute);
}
synchronized(objectToRoute) {
objectToRoute.notifyAll();
}
}
And in your test class there will be something like:
在你的测试课中会有类似的内容:
@Test
public void testMethodToBeTested() throws InterruptedException {
Object objectToRoute = initializeObjectToRoute();
methodToBeTested(objectToRoute);
synchronized (objectToRoute) {
objectToRoute.wait();
}
verifyConditionsAfterRouting(objectToRoute);
}
I do know that in this simple example does not make too much sense, since the sample system is not multithreaded. I assume the multithread twist is added in the routeInOneWay
and routeInAnotherWay
methods; thus, those are the ones to call notifyAll()
method.
我知道在这个简单的例子中没有太大意义,因为示例系统不是多线程的。我假设在routeInOneWay
和routeInAnotherWay
方法中添加了多线程扭曲;因此,那些是调用notifyAll()
方法的那些。
As sample code to point the direction of our solution, here are some code fragments.
作为指明我们解决方案方向的示例代码,这里有一些代码片段。
In the asynchronous worker side, or producer:
在异步工作者端,或生产者:
while(files.hasNext(){
queue.add(files.next());
synchronized (outputQueue) {
queue.notifyAll()
}
}
And in the consumer side:
而在消费者方面:
while(!finished){
while(!queue.isEmpty()){
nextFile = queue.poll();
if (nextFile.equals(NO_MORE_FILES_SIGNAL)) {
finished = true;
break;
}
doYourThingWith(nextFile);
}
if (!finished) {
synchronized (outputQueue) {
outputQueue.wait();
}
}
}
回答by Pusarla Shahil
Instead of avoiding Thread.sleep(), You can pass value as zero in Junit Test Case.
您可以在 Junit 测试用例中将值传递为零,而不是避免 Thread.sleep()。