如何使用JUnit测试触发异步进程的方法?
我不知道如何让我的测试等待流程结束(它不是一个确切的单元测试,它更像一个集成测试,因为它涉及到几个类,而不仅仅是一个)。
如何使用JUnit测试触发异步进程的方法?
我不知道如何让我的测试等待流程结束(它不是一个确切的单元测试,它更像一个集成测试,因为它涉及到几个类,而不仅仅是一个)。
当前回答
我更喜欢使用等待和通知。它简单明了。
@Test
public void test() throws Throwable {
final boolean[] asyncExecuted = {false};
final Throwable[] asyncThrowable= {null};
// do anything async
new Thread(new Runnable() {
@Override
public void run() {
try {
// Put your test here.
fail();
}
// lets inform the test thread that there is an error.
catch (Throwable throwable){
asyncThrowable[0] = throwable;
}
// ensure to release asyncExecuted in case of error.
finally {
synchronized (asyncExecuted){
asyncExecuted[0] = true;
asyncExecuted.notify();
}
}
}
}).start();
// Waiting for the test is complete
synchronized (asyncExecuted){
while(!asyncExecuted[0]){
asyncExecuted.wait();
}
}
// get any async error, including exceptions and assertationErrors
if(asyncThrowable[0] != null){
throw asyncThrowable[0];
}
}
基本上,我们需要创建一个最终的数组引用,在匿名内部类中使用。我宁愿创建一个布尔[],因为如果我们需要wait(),我可以设置一个值来控制。当一切都完成后,我们只需释放asyncExecuted。
其他回答
如果测试结果是异步生成的,这就是我现在使用的方法。
public class TestUtil {
public static <R> R await(Consumer<CompletableFuture<R>> completer) {
return await(20, TimeUnit.SECONDS, completer);
}
public static <R> R await(int time, TimeUnit unit, Consumer<CompletableFuture<R>> completer) {
CompletableFuture<R> f = new CompletableFuture<>();
completer.accept(f);
try {
return f.get(time, unit);
} catch (InterruptedException | TimeoutException e) {
throw new RuntimeException("Future timed out", e);
} catch (ExecutionException e) {
throw new RuntimeException("Future failed", e.getCause());
}
}
}
使用静态导入,测试读起来还不错。 (注意,在这个例子中,我开始一个线程来说明这个想法)
@Test
public void testAsync() {
String result = await(f -> {
new Thread(() -> f.complete("My Result")).start();
});
assertEquals("My Result", result);
}
如果未调用f.f complete,测试将在超时后失败。你也可以使用f.c earteexceptions来提前失败。
值得一提的是,《并发实践》中有一章非常有用,它描述了一些单元测试方法,并给出了解决问题的方案。
如果您使用CompletableFuture(在Java 8中引入)或SettableFuture(来自谷歌Guava),您可以在测试完成后立即完成测试,而不是等待预先设置的时间。你的测试应该是这样的:
CompletableFuture<String> future = new CompletableFuture<>();
executorService.submit(new Runnable() {
@Override
public void run() {
future.complete("Hello World!");
}
});
assertEquals("Hello World!", future.get());
注意,有一个库为pre Java-8提供了CompletableFuture,它甚至使用了相同的名称(并提供了所有相关的Java-8类),例如: net.sourceforge.streamsupport: streamsupport-minifuture: 1.7.4 这对于Android开发很有用,即使我们使用JDK-v11构建,我们也希望保持代码与前Android-7设备兼容。
您可以尝试使用await库。这使得测试您所谈论的系统变得很容易。
JUnit 5有断言。assertTimeout(Duration, Executable)/ asserttimeoutpreempemptive()(请阅读每个的Javadoc以了解差异)和Mockito有verify(mock, timeout(毫秒).times(x))。
Assertions.assertTimeout(Duration.ofMillis(1000), () ->
myReactiveService.doSth().subscribe()
);
And:
Mockito.verify(myReactiveService,
timeout(1000).times(0)).doSth(); // cannot use never() here
管道中的超时可能是不确定的/脆弱的。所以要小心。