JUnit testing an asynchronous method with Mockito
The solution was to add in a Mockito verification step with a timeout and a check to ensure that the mocked component's method had been called the appropriate number of times:
Mockito.verify(myComponentAsAMock, Mockito.timeout(1000).times(1)).doOtherStuff(ArgumentMatchers.any(MyClass.class));
GarlicBread
Updated on June 21, 2022Comments
-
GarlicBread almost 2 years
I have implemented an asynchronous method in a Java 1.8 class using Spring Framework (version 5.0.5.RELEASE):
public class ClassToBeTested { @Autowired private MyComponent myComponent; @Async public void doStuff(List<MyClass> myObjects) { CompletableFuture<MyResponseObject>[] futureList = new CompletableFuture[myObjects.size()]; int count = 0; for (MyClass myObject : myObjects) { futureList[count] = myComponent.doOtherStuff(myObject); count++; } // Wait until all doOtherStuff() calls have been completed CompletableFuture.allOf(futureList).join(); ... other stuff ... } }
I'm trying to test the class with JUnit and Mockito. I have set it up as follows, with an aim of mocking the doStuff() method's call to the component:
@MockBean private MyComponent myComponentAsAMock; @InjectMocks @Autowired private ClassToBeTested classToBeTested; @Test public void myTest() throws Exception { // Create object to return when myComponent.doOtherStuff() is called. CompletableFuture<MyResponseObject> completableFuture = new CompletableFuture<MyResponseObject>(); ... populate an instance of MyResponseObject ... completableFuture.complete(myResponseObject); // Return object when myComponent.doOtherStuff() is called. Mockito.when( myComponentAsAMock.doOtherStuff(ArgumentMatchers.any(MyClass.class))) .thenReturn(completableFuture); // Test. List<MyClass> myObjects = new ArrayList<MyClass>(); MyClass myObject = new MyClass(); myobjects.add(myObject); classToBeTested.doStuff(myObjects); }
Whilst the unit test seems to be successful when I run it individually in Eclipse, but doing a Maven build of the whole project I notice NullPointerExceptions are being thrown:
[ThreadExecutor2] .a.i.SimpleAsyncUncaughtExceptionHandler : Unexpected error occurred invoking async method 'public void package.ClassToBeTested.doStuff(java.util.List)'. java.lang.NullPointerException: null at java.util.concurrent.CompletableFuture.andTree(CompletableFuture.java:1306) ~[na:1.8.0_131] at java.util.concurrent.CompletableFuture.allOf(CompletableFuture.java:2225) ~[na:1.8.0_131] at package.ClassToBeTested.doStuff(ClassToBeTested.java:75) ~[classes/:na]
The error is being raised on this line of ClassToBeTested.java:
CompletableFuture.allOf(completedFutureList).join();
It looks like the exception message is being displayed in the Maven build output after the test has finished (there are other tests being run whose output occur before the error message is being displayed), so I'm guessing it's something to do with the fact that the call to doStuff() is asynchronous.
Any assistance would be appreciated.