Hot questions for Using Mockito in future
Question:
I've got a class HttpClient
that has a function that returns CompletableFuture
:
public class HttpClient { public static CompletableFuture<int> getSize() { CompletableFuture<int> future = ClientHelper.getResults() .thenApply((searchResults) -> { return searchResults.size(); }); return future; } }
Then another function calls this function:
public class Caller { public static void caller() throws Exception { // some other code than can throw an exception HttpClient.getSize() .thenApply((count) -> { System.out.println(count); return count; }) .exceptionally(ex -> { System.out.println("Whoops! Something happened...."); }); } }
Now, I want to write a test to simulates that ClientHelper.getResults
fails, so for that I wrote this:
@Test public void myTest() { HttpClient mockClient = mock(HttpClient.class); try { Mockito.doThrow(new CompletionException(new Exception("HTTP call failed"))) .when(mockClient) .getSize(); Caller.caller(); } catch (Exception e) { Assert.fail("Caller should not have thrown an exception!"); } }
This test fails. The code within exceptionally
never gets executed. However, if I run the source code normally and the HTTP call does fail, it goes to the exceptionally
block just fine.
How must I write the test so that the exceptionally
code is executed?
Answer:
I got this to work by doing this in the test:
CompletableFuture<Long> future = new CompletableFuture<>(); future.completeExceptionally(new Exception("HTTP call failed!")); Mockito.when(mockClient.getSize()) .thenReturn(future);
Not sure if this is the best way though.
Question:
I'm learning about java 8 CompletableFuture
and ended up with this.
Fist of all, what do you think about this lines of code? I need to send request to different services in parallel and then wait for all of them to response 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();
And even the code is doing what I want, I'm having problems testing the class where that code is. I tried using Mockito and do something like:
doAnswer(invocation -> CompletableFuture.completedFuture(this.serviceAResponse)) .when(this.serviceAExecutorService) .execute(any());
Where executor services and services responses are mocking but the test never ends and the thread keeps waiting for something in this line
CompletableFuture.allOf(serviceAFuture, serviceBFuture).join();
Any hint on what I'm missing here? Thank you!
Answer:
If I were you, I would simply mock the services A and B and your executors, then inject them thanks to the annotation @InjectMocks
as they are fields of your class.
If you want mock the method execute
of your Executor
, you should rather proceed as next to simply call the method run
of the provided Runnable
:
doAnswer( (InvocationOnMock invocation) -> { ((Runnable) invocation.getArguments()[0]).run(); return null; } ).when(serviceAExecutorService).execute(any(Runnable.class));
So basically your test would be something 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()); } }
Question:
The following code returns a Future.
val findUserFuture: Future[Option[User]] = userRepo.findOne(userKeys)
Then I process the Future
findUserFuture.flatMap {....} .recover{...}
fineOne
returns the Future
and the Future
wraps call to getOneById
def findOne(userKeys:UserKeys):Future[Option[User]] = { Future{ //val loginInfo:LoginInfo = LoginInfo(userKeys.providerID,userKeys.authProvider) val userOption:Option[User] = getOneById(userKeys) userOption } }
I suppose that recover will be called if Future
returned by findOne
fails i.e. throws an Exception. So I am simulating that by making getOneById
throw an exception.
when(mockUserRepository.findOne(userKeys)).thenReturn(Future(Some(user))) when(mockUserRepository.getOneById(userKeys)).thenThrow(classOf[RuntimeException]) //simulating database error
But the unit test doesn't throw an exception and the test proceeds using value Future(Some(User))
.
I also tried throwing the exception from findOne
- when(mockUserRepository.findOne(userKeys)).thenThrow(classOf[RuntimeException])
but the test case stops
with the following two prints and the .recover
of the Future
is not called
java.lang.RuntimeException was thrown. java.lang.RuntimeException
Answer:
This findUserFuture: Future[Option[User]]
or userRepo.findOne
returns future,
hence you need to return Future.failed
in your mock.
For ex.
when(mockUserRepository.findOne(userKeys)).thenReturn(Future(Some(user))) when(mockUserRepository.getOneById(userKeys)).thenReturn(Future.failed(new RuntimeException("network failure"))
Find below complete working test to simulate your use case :
test("mock future test") { case class User(name: String) case class UserNotFoundException(name: String) extends Exception trait UserRepo { def findOne(name: String): Future[Option[User]] } val name = "bob" val dummyUser = User("dummy") val userRepo = mock[UserRepo] when(userRepo.findOne(name)).thenReturn(Future.failed(new RuntimeException())) val userF = userRepo .findOne(name) .flatMap { case Some(user) ⇒ Future.successful(user) case None ⇒ Future.failed(UserNotFoundException(name)) } .recover { case NonFatal(_) ⇒ dummyUser } userF.futureValue shouldBe dummyUser }
- Update * After looking at the original post closely, I found small mistake in the way you are mocking. try below:
when(mockUserRepository.findOne(userKeys)).thenCallRealMethod() when(mockUserRepository.getOneById(userKeys)).thenThrow(classOf[RuntimeException])
Notice thenCallRealMethod()
here, earlier you were mocking findOne
to return future with successful value which means original method was not getting called which in turn was not calling getOneById
Question:
I'm trying to unit test some of my client code that looks like the following:
@Override public void stop() { if (client != null) { Future<Void> disconnectClient = client.disconnect(); try { disconnectClient.await(); } catch (InterruptedException e) { logger.info("Failed to disconnect client: ", e.getLocalizedMessage()); } } else { logger.error("Client does not exist."); } }
where the disconnect()
returns a Future<Void>
. A simple test looks like:
@Mock private MyClient client; @InjectMocks private MyServer objectUnderTest = new MyServer(); @Test public void shouldCallDisconnectOnClientWhenDefined() throws Exception { objectUnderTest.stop(); verify(client, times(1)).disconnect(); }
When I run this, I obviously get a NPE on the Future
object. I cant think of any when()
code that can successfully return me a mocked Future
or anything like that. How could I get this?
MyServer
contains a client
connector, that connects to an outside server upon initialisation of MyServer
. Multiple MyServer
s can be created, each with their own channel to the outside server, through their client
. All of the instances of MyServer
are maintained by a MyServerRegistry
. When a connection is no longer required, the client
will disconnect, and the MyServer
instance removed from the registry.
Note! client
is not Autowired into MyServer, but instead initalised and a connection opened in the constructor of MyServer
. I also have no control over the (non-final) disconnect()
call returning a Future<Void>
.
Answer:
What about the simple:
when(client.disconnect()).thenReturn(mockedFuture);
Or am I overlooking something here? You can verify the method call, so why do you think you can't mock it?!
The client
object is your object. You already mocked it. So what makes you think you don't have control over its disconnect()
method? That method is called on your mock; it returns a value, so you can mock it!
( unless MyClient
or disconnect()
is final. then you need to use newer versions of Mockito 2 supporting "final-mocking" as experimental feature )
Question:
I have to runnable function in Completable Future with timeout. The runnable function should be invoked only when the original method takes more than the given timeout. The unit keeps giving= Wanted but not invoked: However, there were exactly 3 interactions with this mock.
All I am trying to do is, I am trying to add timeout for a method execution (getResponseWithTimeoutFunction(itemRequest)) and if the method takes more time, then terminate it and publish count(to understand the timed out response rate) as metric.
@Test public void testTimeoutFunction() throws Exception { Response response = getResponseForTest(); when(requestAdapter.transform(itemRequest)).thenReturn(Request); when(dataProvider .provide(any(Request.class))) .thenAnswer((Answer<Response>) invocation -> { Thread.sleep(1000000); return response; }); processor = spy(getProcessor()); when(itemRequest.getRequestContext()).thenReturn(itemRequestContext); when(itemRequestContext.getMetadata()).thenReturn(requestContextMetadata); List<Item> output = processor.getItemist(ITEM_ID, itemRequest); assertTrue(output.isEmpty()); verify(processor, times(1)).processRequest(Request); verify(processor, times(1)).responseTimedOutCount(); }
This is method which I am testing for:
public class Process { @VisibleForTesting void responseTimedOutCount() { //log metrics } private CompletableFuture<Response> getResponseAsync(final ScheduledExecutorService delayer, final ItemRequest itemRequest) { return timeoutWithTimeoutFunction(delayer, EXECUTION_TIMEOUT, TimeUnit.MILLISECONDS, CompletableFuture.supplyAsync(() -> getResponseWithTimeoutFunction(itemRequest), executorService), Response.emptyResponse(), () -> responseTimedOutCount()); } private Response getResponseWithTimeoutFunction(final ItemRequest itemRequest) { //do something and return response } public List<Item> getItemList(final String id, final ItemRequest itemRequest) throws Exception { final ScheduledExecutorService delayer = Executors.newScheduledThreadPool(1); Response response; if(validateItemId(id){ try { response = getResponseAsync(delayer, itemRequest).get(); } catch (final Throwable t) { response = Response.emptyResponse(); } finally { delayer.shutdown(); } return transform(response, id).getItems(); } else { return null; } } }
And the timeout function use =
public static <T> CompletableFuture<T> timeoutWithTimeoutFunction(final ScheduledExecutorService es, final long timeout, final TimeUnit unit, final CompletableFuture<T> f, final T defaultValue, final Runnable r) { final Runnable timeoutFunction = () -> { boolean timedOut = f.complete(defaultValue); if (timedOut && r != null) { r.run(); } }; es.schedule(timeoutFunction, timeout, unit); return f; }
Exception from Junit :
Wanted but not invoked: process.responseTimedOutCount(); -> at processTest.testTimeoutFunction(processTest.java:377) However, there were exactly 3 interactions with this mock: process.getItemList( ITEM_ID, itemRequest ); -> at processTest.testTimeoutFunction(processTest.java:373) process.validateItemId( ITEM_ID ); -> at process.getItemList(process.java:133) process.processRequest( request ); -> at process.getResponseWithTimeoutFunction(process.java:170)
Answer:
To test timeouts you probably want to mock the call you want the timeout tested for. Relative to the duration of the test it should take forever.
when(dataProvider .provide(any(Request.class))) .thenAnswer((Answer<Response>) invocation -> { Thread.sleep(FOREVER); return response; });
The verify should have a timeout for threading handling. When the timeout is long, you probably should make sure it is configurable to allow a fast test. Something like verify(mock, timeout(LONGER_THAN_REAL_TIMEOUT)).someCall()
Make sure to put a timeout on the total test duration to make sure that current or future failures will not slow down your builds.
Question:
I'm trying to do unit test for the following couchbase method call, I couldn't find a satisfactory sample here in SO, so I thought I'd post a question....
def getUserSession(token: String, ipAddr: String, userAgent: Option[String]): Future[Option[UserSession]] = { val query = new Query().setIncludeDocs(true).setLimit(1) .setRangeStart(ComplexKey.of(token)) .setRangeEnd(ComplexKey.of(s"$token\uefff")) .setStale(Stale.FALSE) bucket.find[UserSession](DOC_NAME, VIEW_BY_TOKEN)(query).map(_.headOption) map { userSessionOpt => { userSessionOpt filter { userSession => (userSession.ipAddr == ipAddr) && (!userAgent.isDefined || !userSession.userAgent.isDefined || userSession.userAgent == userAgent) } } }
}
So this is my attempt, my unit test excerpt:
val mockQueryResult = mock[Future[List[UserSession]]] val mockUserSessionList = mock[List[UserSession]] val mockUserSession = mock[UserSession] // This is just my trial and erros mockUserSessionList.head returns mockUserSession mockUserSessionList(0) returns mockUserSession Await.result(mockQueryResult, Duration(60, SECONDS)) returns mockUserSessionList mockBucket.find[UserSession](any[String], any[String])(any[Query])(any[Reads[UserSession]], any[ExecutionContext]) returns mockQueryResult val queryCaptor = capture[Query] val readsCaptor = capture[Reads[UserSession]] val executionContextCaptor = capture[ExecutionContext] val docNameCaptor = capture[String] val viewNameCaptor = capture[String] userSessionRepositoryWithMockBucket.getUserSession(TEST_SESSION_TOKEN1, TEST_IP_ADDR1, TEST_USER_AGENT) there was one(mockBucket).find[UserSession](docNameCaptor, viewNameCaptor)(queryCaptor)(readsCaptor, executionContextCaptor)
Now at some point in the bucket.find
method call is throwing NPE. I did a little digging and it seems like the Await.result
returns mocking doesn't really work in returning value for map
probably for obvious reasons. It does return result if I modified my getUserSession function to use Await.result
. I was just trying it out 'cause I haven't creatively figured out other ways (this is new to me).
There seems to be a less verbose ways of doing the mocking instead of having to do all the layers of Future[List[UserSession]]
. Thanks for your time!
Answer:
I think I might've found the answer to how to mock future that work with map operation. Seems like if we create a 'successful' Future, it will work as follows:
val mockUserSession = createTestUserSession val mockUserSessionList = mock[List[UserSession]] val mockUserSessionListFuture = Future.successful[List[UserSession]](mockUserSessionList) mockUserSessionList.headOption returns Some(mockUserSession)
This will in turn allows the map to in the getUserSession
to be supplied with proper instance of the headOption
which in this case is Some(mockUserSession)