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()); } }