I'm making call to an external system via a static method
MyExternalServiceAccessor.myMethod(param1, param2);
And so far I've been unit-testing above using PowerMockito's verifyStatic as follows
import static org.mockito.Matchers.eq;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.verifyStatic;
..
mockStatic(MyExternalServiceAccessor.class);
..
verifyStatic();
MyExternalService.myMethod(eq(arg1), eq(arg2));
I now want to make above call 'async' (best-effort call with fire-and-forget semantics, I don't care for the response); so taking reference from here I wrapped it inside CompletableFuture.runAsync(..) as follows
import java.util.concurrent.CompletableFuture;
CompletableFuture.runAsync(() -> {
MyExternalServiceAccessor.myMethod(param1, param2);
});
But with this change as expected the unit-test has become flaky because due to the async invocation of MyExternalServiceAccessor.myMethod(..), sometimes validation fails with following error
java.lang.RuntimeException: Wanted but not invoked com.company.team.service.serviceutils.MyClass.myFunctionBeingUnitTested(
null,
null
);
Actually, there were zero interactions with this mock.
- I'm aware that
Mockitosupports verification with timeout for non-staticmethods; is there aPowerMockito(orMockito) equivalent of the same forstaticmethods? - If not then what are the alternatives? Can I cleverly rewrite my code and tests in a way to keep it async while still having unit-tests pass everytime?
I think I might've found a fix by using a non-static method call being made after my static method call as a 'proxy' for hinting that our static method call must've also happened
I was logging an error metric in case my static method call fails as follows
Now in above code
metricLogger.logCounter(..)call is a non-static call for which we can leverage mockito's verification with timeoutMyExternalServiceAccessor.myMethod(..)call so we can be sure that iflogCountercall has happened thenmyMethodcall must've also happenedmyMethodthrows an exceptionTherefore for an error scenario we write our test like this for waiting 100ms before validating that expected methods invoked
And for non-error scenario we can use the trick suggested in this thread to wait-and-verify that our mock was called 'zero' times, i.e., not called even after waiting
So far my tests have been passing, but since (a) they were not too flaky to begin with and (b) we're dealing with notorious async issues that can show up intermittently, I'm not certain if for non-error scenarios the flakiness has been completely eliminated or not.
(I can of course completely do away with the flakiness for example by also publishing a counter metric in success scenario and using that as a proxy, but don't want to do that just yet)
UPDATE-1
I also had to test out the scenario where this entire block of async code is never called (due to upstream conditional expressions preventing flow from reaching up till this point where async block is triggered)
For that I leveraged
Mockito.never()as suggested in this thread