如何在JUnit测试用例中处理wait()方法调用?

时间:2017-04-03 06:23:47

标签: java multithreading unit-testing junit mockito

我有一个方法,它有另一个方法调用,其定义有一个SomeIntegerObject.wait()调用。现在,当我运行我的测试用例时,由于这种等待方法,测试用例一直在等待并且不会执行。

我尝试在@Test注释中添加超时,但这会导致类似"测试超时"等异常。

测试方法:

private PastingResult result;
public PastingResult pasteImages(IIOImage[] images, byte pasteProcessType){
     result = new PastingResult();
     _reprocessingProvider.setListLogicHandler(this);
     //--some more method calls--
    waitForResult(); //causes my test case to wait forever and pauses execution
    return result;
}

测试用例:

@Test
    public void testPasteImagesIIOImageArrayByte() throws Exception 
    {
        ReprocessManager _reprocessingProvider1=Mockito.mock(ReprocessManager.class);
        Whitebox.setInternalState(imagePasterIfImpl, "_reprocessingProvider", _reprocessingProvider1);

        IIOImage[] images=new IIOImage[]{Mockito.mock(IIOImage.class),Mockito.mock(IIOImage.class)};
        byte pasteProcessType = 02;

        PastingResult result= imagePasterIfImpl.pasteImages(images, pasteProcessType);  
        Mockito.verify(_reprocessingProvider1).setListLogicHandler(imagePasterIfImpl);
        System.out.println(result+"is the result");
    }

waitForResult();

的定义
private Integer processingWaitMonitor = new Integer(0);
private void waitForResult() {
       // synchronize to wait on the Monitor..
       synchronized(processingWaitMonitor) {
           try {
               processingWaitMonitor.wait();
           }
           catch(Exception e) {
               System.out.println("Exception during Wait !"+e);
           }
       }
   }

2 个答案:

答案 0 :(得分:3)

这里有两个答案:

  • 在第一级:如果你需要控制被测对象的“内部”;你可以转向Mockito Spys或PowerMock或JMockit;因为这些框架允许在这些方面“嘲笑”(也就是获得控制)。但在大多数情况下,这并不是一个好的做法;如果有的话,你应该将所需的功能“外包”到另一个类中;然后使用依赖注入,以便为您的测试设置提供该类的模拟对象。而不是真正的等待,你只需要在一个没有做任何事情的模拟对象上调用一些等待方法。
  • 除此之外:您可以退后一步,考虑重新设计整个方法。你看,使用那些“低级”原语如wait / notify再也不是练习了。有一些抽象,例如 ExecutorService ,Futures,Promises。关键是:使用ExecutorService,您可以重写代码以使用Same-Thread-Executor-Service进行测试。含义:可以重写多线程代码,这样只需为测试提供不同的执行程序,所有事情都会在单个线程上发生。没有更多的等待,保证,可靠的结果。

关于你的评论;你的代码说:

private Integer processingWaitMonitor = new Integer(0);
private void waitForResult() {

含义:您正在测试的类中有一个字段+私有方法。假设你创建了

public interface<E> WaitService {
   public E waitForResult();
}

或类似的东西。然后;而不是拥有一个锁对象,以及生产类中的wait方法,你只保留那个服务的“某个”实例。但是,当然,这又要求您更改生产代码。

如果你真的无法改变那段代码(这是一种耻辱 - 当你无法改变时,测试代码的重点是什么?)......那么Mockito / PowerMock / JMockit是你唯一的选择那里。

答案 1 :(得分:1)

正在等待的模块,把它放在junit测试用例的新线程中。间隔后,检查线程是否完成。设置间隔的最大超时。发生超时后,检查线程是否完成,如果要传递或失败,请做出决定。

例如:

private static final int MAX_WAIT_TIME = 20000; //total time out in milliseconds
private static final int WAIT_INTERVAL = 1000; //wait interval till MAX_WAIT_TIME

@Test
public void testPasteImagesIIOImageArrayByte() throws Exception 
{
  Thread t = new MyThread();
  t.start();    
  int totalWaitTime = 0;
  while ((!t.isCompleted()) && totalWaitTime <= MAX_WAIT_TIME) {
    try {
        totalWaitTime += WAIT_INTERVAL;
        Thread.sleep(WAIT_INTERVAL);
    } catch (java.lang.InterruptedException e) {
        //LOG OR THROW
    }
    if(!t.isCompleted()){
      if(t.hasExceptionOccurred()){
        //fail the case
      }else{
        //take your decision here, if you want to pass it or fail it
      }
    }
  }
}

class MyThead extends Thread{
  private volatile boolean isCompleted = false;
  private volatile Throwable exception = null;
  private volatile boolean hasExceptionOccured = false;

  public void run(){
    try{
        ReprocessManager _reprocessingProvider1=Mockito.mock(ReprocessManager.class);
        Whitebox.setInternalState(imagePasterIfImpl, "_reprocessingProvider", _reprocessingProvider1);

        IIOImage[] images=new IIOImage[]{Mockito.mock(IIOImage.class),Mockito.mock(IIOImage.class)};
        byte pasteProcessType = 02;

        PastingResult result= imagePasterIfImpl.pasteImages(images, pasteProcessType);  
        Mockito.verify(_reprocessingProvider1).setListLogicHandler(imagePasterIfImpl);
        System.out.println(result+"is the result");

        synchronized(this){
          isCompleted = true;
        }
    }catch(Throwable ex){
        synchronized(this){
            exception = ex;
            hasExceptionOccured = true;
        }
    }
  }

  public synchronized boolean isCompleted(){
    return isCompleted;
  }

  public synchronized boolean hasExceptionOccurred(){
    return hasExceptionOccured;
  }

  public synchronized Throwable getExceptionOccurred(){
    return exception;
  }
}