2017-07-04 20 views
0

非同期呼び出しでconnector.runSomeService(data)を呼び出し、メソッドhandleServiceResponse(res, node)で応答を処理するメソッドがあります。内部CompletableFutureを使ったメソッドのユニットテスト

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()と照合の時間がインターレースできることは明らかです。この2つのアクションの順序は決定的ではありません。

スリープサックを使用する。すべての応答を収集しようとしたとfuture.get()を呼び出す

// when 
engine.runServiceOnAllNodes(data, collectors); 
for (CompletableFuture future : engine.getResponses()) { 
    future.get(); 
} 

が、私はいくつかの例外を取得していますが、私はまだこの方法でも、それはない、吸うという感覚を持っていますか?

答えて

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()を呼び出すの問題です。

関連する問題