早上好,
我不太了解CompletableFutures(我是一位经验丰富的开发人员,但是我发现它们并不特别直观!)。
给出以下代码段:
public CompletionStage<Void> leaveGame(GameService gameService)
{
return gameService.deregister(playerName)
.exceptionally(t -> {
LOGGER.info("Could not deregister: {}", t.getMessage());
throw new CompletionException(t);
});
}
由单元测试调用:
@Test
public void shouldCompleteExceptionallyForFailedLeave()
{
var failFlow = new CompletableFuture<Void>();
failFlow.completeExceptionally(new Exception("TestNonExistentPlayer"));
when(mockedGameService.deregister(any(String.class))).thenReturn(failFlow);
try
{
player.leaveGame(mockedGameService).toCompletableFuture().get();
fail("Exception should have been thrown");
}
catch (Exception e)
{
assertEquals(Exception.class, e.getCause().getClass());
}
verify(mockedGameService, times(1)).deregister(any(String.class));
}
模拟gameService.deregister(...)
以异常完成并返回Exception
。
在上述情况下,如预期的那样,异常分支被触发,消息被记录,并且捕获了单元测试中的异常,即,未触发fail(...)
断言。
但是,当我想在离开游戏前运行CompletionStage时,例如:
public CompletionStage<Void> leaveGame(GameService gameService)
{
return CompletableFuture.runAsync(() -> System.out.println("BLAH"))
.thenRun(() -> gameService.deregister(playerName)
.exceptionally(t -> {
LOGGER.info("Could not deregister: {}", t.getMessage());
throw new CompletionException(t);
}));
}
异常分支is仍然被触发,但是测试方法现在未捕获异常,即fail(...)
断言is被触发。
我在做什么错?
非常感谢!
使用您的原始定义
public CompletionStage<Void> leaveGame(GameService gameService)
{
return gameService.deregister(playerName)
.exceptionally(t -> {
LOGGER.info("Could not deregister: {}", t.getMessage());
throw new CompletionException(t);
});
}
leaveGame
方法未引发异常,但始终返回将来。调用方必须检查将来,以查明封装操作是否失败。
同样,当您将相同的代码移到Runnable
中时,>
public CompletionStage<Void> leaveGame(GameService gameService) { return CompletableFuture.runAsync(() -> System.out.println("BLAH")) .thenRun(() -> gameService.deregister(playerName) .exceptionally(t -> { LOGGER.info("Could not deregister: {}", t.getMessage()); throw new CompletionException(t); })); }
Runnable
不会引发异常。仍然需要检查gameService.deregister(…).exceptionally(…)
返回的未来,以了解是否失败,但是现在,您不是退还而是删除参考。
要创建一个未来,其完成取决于功能评估返回的未来,您需要thenCompose
:
thenCompose
所以现在您实现的是
public CompletionStage<Void> leaveGame(GameService gameService) { return CompletableFuture.runAsync(() -> System.out.println("BLAH")) .thenCompose(voidArg -> gameService.deregister(playerName) .exceptionally(t -> { LOGGER.info("Could not deregister: {}", t.getMessage()); throw new CompletionException(t); })); }
而不是Function<Void,CompletionStage<Void>>
,并且函数返回的阶段将用于完成Runnable
返回的未来。