我有一个方法:
public sendMessageAsync(final PublishRequest request) {
final CompletableFuture<PublishResponse> responseCompletableFuture = m_serviceAClient.publish(request);
responseCompletableFuture.whenCompleteAsync((response, exception) -> {
if (exception != null) {
try {
m_serviceBClient.sendMessage(request.getMessage())
} catch (Exception e) {
log.error("Failed to send message to fallback service.");
}
} else {
log.info("Successfully called service client");
}
}
return;
}
单元测试:
public void testsendMessageAsyncWhenServiceAThrowsException() {
CompletableFuture<PublishResponse> failedFuture = CompletableFuture.failedFuture(
new RuntimeException("Failed to publish message to Service A"));
doReturn(failedFuture).when(mockServiceAClient).publish(any(PublishRequest.class));
doReturn("message-id").when(mockServiceBClient).sendMessage(anyString());
MyClass.sendMessageAsync(new PublishRequest("test-message"));
}
我没有看到jacoco单元测试覆盖了m_serviceBClient.sendMessage(request.getMessage())
行。
我相信这是因为在未来回调处理程序执行时,junit测试已经完成,所以代码覆盖工具不会检测到未来回调语句的覆盖。
1条答案
按热度按时间qojgxg4l1#
为了使这种方法可测试,我通常使用CountDownLatch。
例如,您可以通过创建另一个方法(您可以将其设置为私有,但仅在通过适当的注解进行测试时才可见,这样API的用户就看不到它)来修改类,该方法接受CountDownLatch的输入:
然后,在测试中,创建一个计数为1的CountDownLatch,并在测试结束前等待它:
这样,即使方法是异步的,您也可以使其可测试。当然,您将创建一个具有原始签名(没有闩锁)的方法,该方法通过传递一个示例闩锁来简单地调用新方法,该示例闩锁将被生产代码忽略。
为了给予一些进一步的解释,CountDownLatch是一个并发实用程序,允许您在多线程环境中进行递减计数(这意味着一个线程可以倒计时,其他线程将看到相同的值),还允许您。(如果需要,可以使用超时),CountDown在等待的线程被释放之前达到零。这是一种非常实用的方法,让另一个线程做一些工作,而主线程等待该工作完成(在您的情况下,您的测试线程等待可完成的未来线程在完成测试之前做它必须做的事情)