Java – completable future usability and unit testing
I was learning java 8 completable future and finally got this
All fists, what do you think of this line of code? I need to send requests to different services in parallel, then wait for all services to respond and continue working
//service A
CompletableFuture<ServiceAResponse> serviceAFuture = CompletableFuture.supplyAsync(
() -> this.ServiceA.retrieve(serviceARequest),serviceAExecutorService
);
//service B
CompletableFuture<ServiceBResponse> serviceBFuture = CompletableFuture.supplyAsync(
() -> this.ServiceB.retrieve(serviceBRequest),serviceBExecutorService
);
CompletableFuture.allOf(serviceAFuture,serviceBFuture).join();
ServiceAResponse responseA = serviceAFuture.join();
ServiceBResponse responseB = serviceBFuture.join();
Even the code is doing what I want. I have a problem testing the class where the code is located I try to use mockito and do the following:
doAnswer(invocation -> CompletableFuture.completedFuture(this.serviceAResponse))
.when(this.serviceAExecutorService)
.execute(any());
The executor service and service response are mocking, but the test will never end, and the thread has been waiting for this line
CompletableFuture.allOf(serviceAFuture,serviceBFuture).join();
Any hint I'm missing here? thank you!
Solution
If I were you, I would simply simulate services a and B and your executors, and then inject them through the annotation @ injectmocks, because they are the domain of your class
If you want to simulate the method execution of your executor, you should continue to the next step and simply call the provided runnable method to run:
doAnswer(
(InvocationOnMock invocation) -> {
((Runnable) invocation.getArguments()[0]).run();
return null;
}
).when(serviceAExecutorService).execute(any(Runnable.class));
So basically your test will be like this:
@RunWith(MockitoJUnitRunner.class)
public class CompletableFutureServiceTest {
// The mock of my service A
@Mock
private ServiceA ServiceA;
// The mock of my service B
@Mock
private ServiceB ServiceB;
// The mock of your executor for the service A
@Mock
private Executor serviceAExecutorService;
// The mock of your executor for the service B
@Mock
private Executor serviceBExecutorService;
// My class in which I want to inject the mocks
@InjectMocks
private CompletableFutureService service;
@Test
public void testSomeMethod() {
// Mock the method execute to call the run method of the provided Runnable
doAnswer(
(InvocationOnMock invocation) -> {
((Runnable) invocation.getArguments()[0]).run();
return null;
}
).when(serviceAExecutorService).execute(any(Runnable.class));
doAnswer(
(InvocationOnMock invocation) -> {
((Runnable) invocation.getArguments()[0]).run();
return null;
}
).when(serviceBExecutorService).execute(any(Runnable.class));
ServiceAResponse serviceAResponse = ... // The answer to return by service A
// Make the mock of my service A return my answer
when(ServiceA.retrieve(any(ServiceARequest.class))).thenReturn(
serviceAResponse
);
ServiceBResponse serviceBResponse = ... // The answer to return by service B
// Make the mock of my service B return my answer
when(ServiceB.retrieve(any(ServiceBRequest.class))).thenReturn(
serviceBResponse
);
// Execute my method
ServiceResponse response = service.someMethod(
new ServiceARequest(),new ServiceBRequest()
);
// Test the result assuming that both responses are wrapped into a POJO
Assert.assertEquals(serviceAResponse,response.getServiceAResponse());
Assert.assertEquals(serviceBResponse,response.getServiceBResponse());
}
}
