使用Object.wait()
我们已经处理了一些从目录或ZIP归档中获取文件的异步进程。我们在异步进程继续读取时在其他地方使用文件内容。
我们测试它们的方式是在通信对象上使用wait()
--在我们的情况下是一个队列--,这样异步进程就会在有新文件准备好并继续工作时调用queue.notifyAll()
。在另一端,消费者会逐个处理项目,直到队列为空,然后使用queue.wait()
等待更多项目。我们确实使用通过队列传递的特殊对象来指示没有更多要处理的项目。
在您的情况下,我想要检查的对象objectToRoute
并不是在您想要测试的方法内部真正创建的。您可以在测试中对其进行wait()
,并在您想要测试的方法methodToBeTested
内部对其进行notifyAll()
。我知道这会在您的生产代码库中引入额外的代码行,但如果没有人在等待它,它应该是无害的。最终会变成以下形式:
public void methodToBeTested(Object objectToRoute) {
if (someConditions) {
routingService.routeInOneWay(objectToRoute);
} else {
routingService.routeInAnotherWay(objectToRoute);
}
synchronized(objectToRoute) {
objectToRoute.notifyAll();
}
}
在你的测试类中会有类似这样的代码:
@Test
public void testMethodToBeTested() throws InterruptedException {
Object objectToRoute = initializeObjectToRoute();
methodToBeTested(objectToRoute);
synchronized (objectToRoute) {
objectToRoute.wait();
}
verifyConditionsAfterRouting(objectToRoute);
}
我知道在这个简单的例子中并没有太多意义,因为示例系统不是多线程的。我假设多线程的扭曲是在routeInOneWay
和routeInAnotherWay
方法中添加的;因此,那些是调用notifyAll()
方法的方法。
作为指向我们解决方案方向的示例代码,这里有一些代码片段。
在异步工作端或生产者端:
while(files.hasNext(){
queue.add(files.next());
synchronized (outputQueue) {
queue.notifyAll()
}
}
而在消费者方面:
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();
}
}
}
Thread.sleep
并不是一种不良实践,因为你只是模拟时间的流逝,这对于某些单元测试是必要的。人们之所以说使用Thread.sleep
是一种不良实践,是因为有时它被用来尝试解决竞争条件。你只在测试中使用Thread.sleep
,还是在源代码中也使用了? - Ben Green