Reputation: 410
I am trying to test a method that uses CompletableFuture.allOf.
I mock the futures so that they return the needed value when joined.
for some reason which I can't understand. join() of result takes forever.
any help will do!
Thanks in advance.
@Mock private CompletableFuture<String> future1; // for sequence test
@Mock private CompletableFuture<String> future2; // for sequence test
@Mock private CompletableFuture<String> future3; // for sequence test
private BBService bbServiceTest;
@Test
public void testSequence() {
final String[] expectedResults = {"res1", "res2", "res3"};
List<CompletableFuture<String>> futures = new Vector<>();
when(future1.join()).thenReturn(expectedResults[0]);
when(future2.join()).thenReturn(expectedResults[1]);
when(future3.join()).thenReturn(expectedResults[2]);
futures.add(future1);
futures.add(future2);
futures.add(future3);
bbServiceTest.sequence(futures).join();
Assert.assertTrue(future1.isDone());
Assert.assertTrue(future2.isDone());
Assert.assertTrue(future3.isDone());
// for (int i = 0; i < actualResults.size(); i++) {
// Assert.assertEquals(expectedResults[i], actualResults.get(i));
// }
}
sequence method inside BBService:
public <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futuresList) {
return CompletableFuture
.allOf(futuresList.toArray(new CompletableFuture[0])) // (1)
.thenApply(v ->
futuresList.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList())
);
}
Upvotes: 1
Views: 4528
Reputation: 622
Thats because your futures
never get completed. CompletableFuture.allOf
returns a new CompletableFuture that is completed when all of the given CompletableFutures
complete.
future1.complete()
, future2.complete()
and future3.complete()
never get called.
CompletableFuture.join()
returns the result if the future is completed. Because you mock the futures in your code they return the strings defined in expectedResults
on a join
even if they're not completed. But that's exactly the problem. They don't complete. That's why the test doesn't finish.
I adjusted your Test class a little to demonstrate how it works:
public class FutureTest {
@Test
public void testSequence1() {
List<CompletableFuture<String>> futures = getFutures();
CompletableFuture<Void> myCombinedFuture = sequence(futures);
// here we complete each future
futures.forEach(future -> future.complete("future-completed"));
myCombinedFuture.join();
// this works because we completed all futures before
Assert.assertTrue(myCombinedFuture.isDone());
}
@Test
public void testSequence2() {
List<CompletableFuture<String>> futures = getFutures();
CompletableFuture<Void> myCombinedFuture = sequence(futures);
futures.get(0).complete("completed-future1");
futures.get(1).complete("completed-future2");
myCombinedFuture.join();
// this does not work because future3 is not completed -> we wait forever
Assert.assertTrue(myCombinedFuture.isDone());
}
@Test
public void testSequence3() {
List<CompletableFuture<String>> futures = getFutures();
CompletableFuture<Void> myCombinedFuture = sequence(futures);
futures.get(0).complete("completed-future1");
futures.get(1).complete("completed-future2");
// complete future 3 asynchronous after 2 seconds
futures.get(2).completeAsync(() -> {
// wait for 2 seconds
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "completed-future3";
});
myCombinedFuture.join();
// after 2 seconds all futures are completed and the test will pass
Assert.assertTrue(myCombinedFuture.isDone());
}
public CompletableFuture<Void> sequence(
List<CompletableFuture<String>> futuresList) {
return CompletableFuture.allOf(
futuresList.toArray(new CompletableFuture[0]));
}
public List<CompletableFuture<String>> getFutures() {
return List.of(
new CompletableFuture<>(),
new CompletableFuture<>(),
new CompletableFuture<>()
);
}
}
Upvotes: 4