xuanzhui xuanzhui - 7 months ago 32
Java Question

How to unit test java multiple thread

The issue is that I have a method starting a new thread for a time-consuming work. I want to test the callback result, but the child thread may still running, so as a result, what I get is not the right stub.


I think the code may explain itself:


public class JustAClass {
//it is a callback for async
public interface JustACallBack {
void callFunc(JustAResult result);
}

//this is the result interface
public interface JustAResult {
}

//this is a real class for the interface
public class JustAResultReal implements JustAResult{
public JustAResultReal(String content) {this.content = content;}
public String content;
}

//here is the key function
public void threadFunc(final JustACallBack callBack) {
BCCache.executorService.execute(new Runnable() {
@Override
public void run() {
//just to simulate a time-consuming task
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

//now we callback
callBack.callFunc(new JustAResultReal("can you reach me"));
}
});
}
}


and the test function could be(I am using
mockito
):


@Test
public void testThreadFunc() throws Exception {

JustAClass justAClass = new JustAClass();

JustAClass.JustACallBack callBack = Mockito.mock(JustAClass.JustACallBack.class);

justAClass.threadFunc(callBack);

//add this line, we can get the expected result
Thread.sleep(1200);

Mockito.verify(callBack).callFunc(captor.capture());

System.out.println(((JustAClass.JustAResultReal)captor.getValue()).content);
}


I know we can add a
sleep
to wait and expect that the child thread would exit within the period, but could there be a better way? Actually how could I know how long the child thread would take? Setting a very long time can be an approach but just seems not very nice.

Answer

I aggree with @Gimbys comment about this is no longer a unit-test when you start testing the the threading aspect.

Nevertheless it is interesting as a way to integration-test a asynchronous invokation.

To avvoid sleep i tend to use the class CountDownLatch to wait for invokations. In order to count down you need an actuall implementation of the callback interface - so in my example I have made a mock implementation of this.

Since there is no actual methods to fetch the data - i am just testing that it is in fact a instance of the JustAReal interface.

@Test
public void testInvoke() throws Exception {

    final CountDownLatch countDownLatch = new CountDownLatch(1); //1 is how many invokes we are waiting for

    JustAClass justAClass = new JustAClass();
    JustAClass.JustACallBack callBack = new JustAClass.JustACallBack() {
        @Override
        public void callFunc(final JustAClass.JustAResult result) {
            assertNotNull("Result should not be null", result);
            assertTrue("Result should be instance of JustAResultReal", result instanceof JustAClass.JustAResultReal);
            countDownLatch.countDown();
        }
    };

    justAClass.threadFunc(callBack);
    if(!countDownLatch.await(1200, TimeUnit.MILLISECONDS)){
        fail("Timed out, see log for errors");
    }

}