如何单元测试java多线程

时间:2015-11-13 08:46:36

标签: java multithreading unit-testing callback

问题是我有一个方法为一个耗时的工作开始一个新线程。我想测试回调结果,但子线程可能仍在运行,因此,我得到的不是正确的存根。

我认为代码可能会解释自己:

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

,测试功能可以是(我正在使用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);
}

我知道我们可以添加一个sleep来等待并期望子线程会在这段时间内退出,但是可能有更好的方法吗?实际上我怎么知道子线程需要多长时间?设置很长时间可能是一种方法,但似乎不是很好。

2 个答案:

答案 0 :(得分:3)

@ stalet的答案中的一般方法很接近,但由于主线程没有注意到来自单独线程的任何断言失败,因此不能正常工作。因此,您的测试将始终通过,即使它不应该通过。相反,请尝试使用ConcurrentUnit

@Test
public void testInvoke() throws Throwable {
    Waiter waiter = new Waiter();
    JustAClass justAClass = new JustAClass();
    JustAClass.JustACallBack callBack = new JustAClass.JustACallBack() {
        @Override
        public void callFunc(final JustAClass.JustAResult result) {
            waiter.assertNotNull(result);
            waiter.assertTrue(result instanceof JustAClass.JustAResultReal);
            waiter.resume();
        }
    };

    justAClass.threadFunc(callBack);
    waiter.await(1200, TimeUnit.SECONDS);
}

这里的关键是ConcurrentUnit' s Waiter将正确地向主测试线程报告任何断言失败,并且测试将通过或失败。

答案 1 :(得分:0)

我对@Gimbys的评论表示,当你开始测试线程方面时,这不再是一个单元测试。

然而,作为一种集成测试异步调用的方法很有意思。

为避免睡眠,我倾向于使用班级CountDownLatch来等待调用。 为了倒计时,你需要一个回调接口的实现实现 - 所以在我的例子中,我做了一个模拟实现。

由于没有实际的方法来获取数据 - 我只是测试它实际上是JustAReal接口的实例。

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

}
相关问题