我有一种方法,它以异步方式调用connector.runSomeService(data)
并处理方法handleServiceResponse(res, node)
中的响应。
public void runServiceOnAllNodes(Collection<Node> nodes, Object data) {
nodes.parallelStream().forEach(node -> {
CompletableFuture<ResponseEntity> response = CompletableFuture
.supplyAsync(()-> connector.runSomeService(data));
response.exceptionally(ex -> {
log.error("OMG...OMG!!!")
return null;
})
.thenAcceptAsync(res -> handleServiceResponse(res, node));
});
}
private void handleServiceResponse(ResponseEntity res, Node node) {
if (res.isOK) {
node.setOKStatus();
} else {
node.setFailStatus();
}
dbService.saveNode(node);
}
尝试创建单元测试但是当我尝试验证响应是否得到正确处理时,UT的结果是不确定的。
@Test
public void testRunServiceOnAllNodes() {
// given
List<Collector> nodes = Arrays.asList(node1, node2, node3);
when(connector.runSomeService(eq(node1), eq(data))).thenReturn(ResponseEntity.ok().body("{message:OK}"));
when(connector.runSomeService(eq(node2), eq(data))).thenReturn(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(""));
when(connector.runSomeService(eq(node3), eq(data))).thenThrow(new ResourceAccessException(""));
// when
engine.runServiceOnAllNodes(data, collectors);
// then
verify(connector, times(1)).runSomeService(eq(node1), eq(data));
verify(connector, times(1)).runSomeService(eq(node2), eq(data));
verify(connector, times(1)).runSomeService(eq(node3), eq(data));
verifyNoMoreInteractions(connector);
assertEquals(node1.getStatus(), "OK");
assertEquals(node2.getStatus(), "Fail");
}
它可以以一些不同的结果结束,例如。
Wanted but not invoked:
connector.runSomeService(node2);
However, there were other interactions with this mock:
connector.runSomeService(node1);
或
Argument(s) are different! Wanted:
connector.runSomeService(node1);
Actual invocation has different arguments:
connector.deployFileset(node2);
或有时以成功结束。
很明显,执行时间connector.runSomeService()
和验证时间可以交织。这两个动作的顺序不确定。
使用睡眠很糟糕。试图收集所有回复并调用future.get()
// when
engine.runServiceOnAllNodes(data, collectors);
for (CompletableFuture future : engine.getResponses()) {
future.get();
}
但我得到了一些例外,但我仍然觉得这种方式也很糟糕,不是吗?
答案 0 :(得分:1)
我建议更改runServiceOnAllNodes
方法以返回Future
所以您的测试,以及作为奖励的正常客户端,可以明确等待异步行为完成。
public Future<Void> runServiceOnAllNodes(Collection<Node> nodes, Object data) {
return nodes.parallelStream().map(node -> {
CompletableFuture<ResponseEntity> response = CompletableFuture
.supplyAsync(()-> connector.runSomeService(data));
return response.exceptionally(ex -> {
LOGGER.error("OMG...OMG!!!");
return null;
})
.thenAcceptAsync(res -> handleServiceResponse(res, node));
})
.reduce(CompletableFuture::allOf).orElseGet(() -> CompletableFuture.completedFuture(null));
}
在您的测试中,只需要在进行断言和验证之前调用get()
未来。