2

I have a method that has another method call whose definition has a SomeIntegerObject.wait() call. Now when I run my test case, due to this wait method, the test case keeps waiting and doesn't execute.

I tried adding a timeout at @Test annotation but that causes an exception like "test timed out".

The test method:

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 case:

@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");
    }

Definition of 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);
           }
       }
   }
GhostCat
  • 137,827
  • 25
  • 176
  • 248
hushie
  • 417
  • 10
  • 23
  • So, what *do* you expect/want to happen in your case? Let the test succeed even though the code wasn't done waiting? – Erwin Bolwidt Apr 03 '17 at 06:35
  • I want to verify all the method calls that are taking place in the test method, but the test case itself isn't executing, it just keeps waiting – hushie Apr 03 '17 at 06:37
  • In any case, nice question! – GhostCat Apr 03 '17 at 07:07
  • @hushie what did you mean "I want to verify all method calls?", how doesn't test case failed with "test timed out" fulfill the case? – Amber Beriwal Apr 03 '17 at 09:30
  • @AmberBeriwal I am writing a test case for the method I mentioned above, so in that method there are some method calls, when I verify those calls `waitForResult();` call halts execution of my test case. and the "test timed out exception" happened when I just tried adding a timeout of 1000ms at @Test annotation, so I removed the timeout. – hushie Apr 03 '17 at 09:41
  • @hushie isn't it a valid case failure that results could not be generated in 1000ms? If the thread remains waiting, what do you expect as results--pass or failure? – Amber Beriwal Apr 03 '17 at 09:49
  • @AmberBeriwal then what do you suggest I do? – hushie Apr 03 '17 at 09:50
  • @hushie I can suggest solutions for both pass and failure? What do you expect as output for this wait case..pass or failure? – Amber Beriwal Apr 03 '17 at 09:52
  • I want my test case to execute without having to pause when I execute it. – hushie Apr 03 '17 at 09:58

2 Answers2

3

There are two answers here:

  • On a first level: if you need to control something "inside" an object under test; you can either turn to Mockito Spys or PowerMock or JMockit; as those frameworks allow for "mocking" (aka gaining control) on such aspects. But that is not really good practice in most cases; if at all, you should "outsource" the required functionality into another class; and then you use dependency injection in order provide a mocked object of that class for your test setup. And instead of real waiting, you just call some wait method on a mocked object that simply does nothing.
  • Beyond that: you could step back and consider to re-design your whole approach. You see, using those "low level" primitives such as wait/notify is again not good practice any more. There are abstractions such as ExecutorService, Futures, Promises. The point is: using ExecutorService, you might be able to rewrite your code to use a Same-Thread-Executor-Service for your tests. Meaning: it is possible to rewrite multi-threaded code so that just by providing a different executor for tests, all things happe on a single thread. No more waiting, guaranteed, reliable results.

Regarding your comment; your code says:

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

Meaning: you have a field+private method within your class under test. Assume that you create

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

or something like that. And then; instead of having a lock object, and that wait method inside your production class, you only keep "some" instance of that service there. But of course, that again requires you to change your production code.

If you really can't change that code (which is kind of a shame - what is the point of testing code when you can't change it ? ) ... then Mockito/PowerMock/JMockit are your only options get there.

Community
  • 1
  • 1
GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • Unfortunately I cannot modify the production code, but the point you made is really useful. Can you elaborate more on your first point? I did not understand what you meant by "outsource" the required functionality into another class. – hushie Apr 03 '17 at 06:59
  • Oh okay now I understood with the example you just gave, so I have no other way apart from using Mockito/PowerMock/JMockit ? – hushie Apr 03 '17 at 07:18
  • Well. I have outlined the options. If changing code is not an option, you need to look into working around that un-testable design by turning to "framework hammering" as I call it. But please understand: that might actually work out, but it is neither easy, nor robust. Such solutions easily break; and they do not add to much of value. Keep in mind that you want to test code for a reason. – GhostCat Apr 03 '17 at 07:20
  • Turning to PowerMock, Spies ... you have a problem, and instead of fixing it (by changing production code), you "hammer" it away using framework tricks. Like: you got a painting you want to mount to a wall; but you only got a hammer and a screw, but no screw driver. But instead of buying a screw driver, your solution is to "hammer" the screw into the wall, to attach the painting to it. Works, but not really a good strategy. – GhostCat Apr 03 '17 at 07:25
  • Okay understood, so according to what you said, since I cannot get a screw driver to hang the painting on the wall I must use a hammer. – hushie Apr 03 '17 at 07:33
  • Or run away, yes. Or spill some gasoline over the painting and accidentally set it on fire. Or yes, accept reality and start looking into hammering techniques, and which ones works the best in your case. – GhostCat Apr 03 '17 at 07:36
  • I guess I better start looking into some good hammering techniques then – hushie Apr 03 '17 at 07:41
  • Yes. And I think we hammered that stupid metamorphe into the ground by now ... but thats the whole point of hammering ;-) – GhostCat Apr 03 '17 at 07:43
  • Yeah I think so too :D – hushie Apr 03 '17 at 07:51
1

The module which is going in waiting, put that in a new thread in junit test case. After intervals, check if thread is completed or not. Set a maximum timeout for intervals. After the time out happened, check for thread completion and take decision if you want to pass it or fail it.

For example:

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;
  }
}
Amber Beriwal
  • 1,568
  • 16
  • 30
  • I am a bit rusty on thread concepts so pardon the silly questions, but I don't want to create a separate class called `MyThread`. Is there a way to do it without creating a separate class? – hushie Apr 03 '17 at 10:16
  • It will need a new thread in any case. If you do not want to go into thread thing by yourself then you can use the abstract layer of threads as suggested by GhostCat. – Amber Beriwal Apr 03 '17 at 10:19
  • it is guaranteed that above sample will work, you must set a time out. Infact, Junit is doing the same for you, if you set a timeout. If you do it yourself, you have the control if you wanna pass it or fail it. – Amber Beriwal Apr 03 '17 at 10:20
  • Alright, I do know it will work for sure. But creating another class and then another set of methods to do it makes it seem a bit tedious. I wanted to find a way that is much less tedious and easier to understand when someone goes through my test case. – hushie Apr 03 '17 at 10:23
  • @hushie I think JUnit TimeOutException will be simplest and most compact among all the available options.. – Amber Beriwal Apr 04 '17 at 05:27