如何对java多线程进行单元测试

j2cgzkjk  于 2022-12-21  发布在  Java
关注(0)|答案(2)|浏览(144)

问题是我有一个方法在启动一个新的线程,这是一个耗时的工作。我想测试回调结果,但是子线程可能还在运行,所以结果是,我得到的不是正确的存根。
我想密码可以解释:

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来等待并期望子线程在该时间段内退出,但有没有更好的方法?实际上,我怎么知道子线程将花费多长时间?设置一个很长的时间可能是一种方法,但似乎不是很好。

zu0ti5jz

zu0ti5jz1#

@stalet的答案中的一般方法很接近,但并不完全有效,因为主线程不会注意到来自单独线程的任何Assert失败,因此您的测试总是通过,即使它不应该通过,而是尝试使用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的Waiter将正确地向主测试线程报告任何Assert失败,并且测试将按照它应该的方式通过或失败。

dly7yett

dly7yett2#

我同意@Gimbys的评论,当您开始测试线程方面时,这不再是一个单元测试。
然而,作为一种集成测试异步调用的方法,它还是很有趣的。
为了避免睡眠,我倾向于使用类CountDownLatch来等待调用,为了倒计时,你需要一个回调接口的actuall实现--所以在我的例子中,我做了一个模拟实现。
由于没有实际的方法来获取数据-我只是测试它实际上是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");
    }

}

相关问题