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 MyServers 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)