问题是我有一个方法为一个耗时的工作开始一个新线程。我想测试回调结果,但子线程可能仍在运行,因此,我得到的不是正确的存根。
我认为代码可能会解释自己:
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
来等待并期望子线程会在这段时间内退出,但是可能有更好的方法吗?实际上我怎么知道子线程需要多长时间?设置很长时间可能是一种方法,但似乎不是很好。
答案 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");
}
}