内部具有CompletableFuture的方法的单元测试

时间:2017-07-04 14:00:59

标签: java unit-testing asynchronous completable-future

我有一种方法,它以异步方式调用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();
}

但我得到了一些例外,但我仍然觉得这种方式也很糟糕,不是吗?

1 个答案:

答案 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()未来。

相关问题