4

I need help to write mockito test case for below method.

public void getCouponAndNotifyAsync(String countryId, String channelId,
        String storeNumber, String clientId, NotificationRequest notificationRequest)
        throws FirestoreException, TurneroServiceException {
    CompletableFuture.runAsync(() -> getCouponAndNotify(countryId, channelId,
            storeNumber, clientId, notificationRequest));
}

Where getCouponAndNotify() is a void method.

Tried below but its not working

@Test
    public void getCouponAndNotifyAsync() throws Exception {
        //doNothing().when(turneroService).getCouponAndNotify(COUNTRYID, CHANNELID, STORENUMBER, CLIENTID, new NotificationRequest("ext_rborse@falabella.cl", "all"));

        CompletableFuture<Void> runAsync = CompletableFuture
                .runAsync(() -> doNothing().when(turneroService).getCouponAndNotify(COUNTRYID, CHANNELID, STORENUMBER, CLIENTID, new NotificationRequest("ext_rborse@falabella.cl", "all")));

        assertTrue(runAsync.isDone());

    }

Updated test cases but still not working.

@Test
    public void getCouponAndNotifyAsync() throws Exception {
        //doNothing().when(turneroService).getCouponAndNotify(COUNTRYID, CHANNELID, STORENUMBER, CLIENTID, new NotificationRequest("ext_rborse@falabella.cl", "all"));

        CompletableFuture<Void> runAsync = CompletableFuture
                .runAsync(() -> doNothing().when(turneroService).getCouponAndNotify(COUNTRYID, CHANNELID, STORENUMBER, CLIENTID, new NotificationRequest("ext_rborse@falabella.cl", "all")));

        assertTrue(ForkJoinPool.commonPool().awaitQuiescence(5, TimeUnit.SECONDS));
        assertTrue(runAsync.isDone());

    }
Rahul
  • 493
  • 3
  • 7
  • 25
  • Are you trying to use a [partial mock](https://stackoverflow.com/questions/14970516/use-mockito-to-mock-some-methods-but-not-others)? You might be better off extending the class in your test and overriding the synchronous method. – David Ehrmann Mar 28 '19 at 07:12
  • Where are you calling `getCouponAndNotifyAsync()` in your test? I also don't think the mock works like you think it does. – David Ehrmann Mar 28 '19 at 07:14
  • Can you please explain more on this? getCouponAndNotifyAsync() is getting called in controller class. That test is passed. – Rahul Mar 29 '19 at 06:32

3 Answers3

5

I assume you're testing getCouponAndNotify() elsewhere so you don't have to worry about it throwing an exception.

What you'll run into is a race condition between getCouponAndNotifyAsync() and getCouponAndNotify() returning. There are a few solutions for this:

Since you're using the common ForkJoinPool, do

assertTrue(ForkJoinPool.commonPool().awaitQuiescence(5, TimeUnit.Seconds));

It waits for the task to finish.

Alternatively, you could inject an ExecutorService and use it as the second parameter to supplyAsync(). You have a few choices: you could use a mock, you could use an ExecutorService that runs with the current thread, or you could inject a standard Executors.newSingleThreadExecutor(), then call shutdown() and awaitTermination() in your test.

You could also return a CompletionStage<Void> from getCouponAndNotifyAsync() that you can wait on in the test.

David Ehrmann
  • 7,366
  • 2
  • 31
  • 40
1

Let's say you have this code:

public void method() {
        CompletableFuture.runAsync(() -> {
            //logic
            //logic
            //logic
            //logic
        });
    }

Try to refactor it to something like this:

public void refactoredMethod() {
    CompletableFuture.runAsync(this::subMethod);
}

private void subMethod() {
    //logic
    //logic
    //logic
    //logic
}

After that, test the subMethod this way:

org.powermock.reflect.Whitebox.invokeMethod(classInstance, "subMethod"); 
Mockito.verify(...)

This isn't a perfect solution, but it tests all the logic inside your async execution.

Rostislav V
  • 1,706
  • 1
  • 19
  • 31
1

To verify your asynchronous method is called, you can use

 verify(<<your service>>, **timeout(100)**.times(1))
      .<<your method>>(<< arguments>>);

ex:-

verify(service, **timeout(100)**.times(1))
      .getCouponAndNotify(anyString(), anyString(), anyString(), anyString(), any(NotificationRequest.class));

Refer https://www.javadoc.io/doc/org.mockito/mockito-core/2.2.9/org/mockito/verification/VerificationWithTimeout.html

achini
  • 419
  • 4
  • 7