简体   繁体   中英

mocking ExecutorService invokeAll method in java

I am trying to mock the ExecutorService but it is constantly giving compile error. The code that I am trying is the following:

ExecutorService executorService = mock(ExecutorService.class);
Future<A> mockedFuture = mock(Future.class);
List<Future<A>> list = Lists.newArrayList(mockedFuture);

when(executorService.invokeAll(any())).thenReturn(list);

When I compile this, it returns the following error:

error: no suitable method found for thenReturn(List<Future<A>>)
[javac]         when(executorService.invokeAll(any())).thenReturn(futureList);
[javac]                                               ^
[javac]     method OngoingStubbing.thenReturn(List<Future<Object>>) is not applicable
[javac]       (argument mismatch; List<Future<A>> cannot be converted to List<Future<Object>>)
[javac]     method OngoingStubbing.thenReturn(List<Future<Object>>,List<Future<Object>>...) is not applicable
[javac]       (argument mismatch; List<Future<A>> cannot be converted to List<Future<Object>>)

any idea?

Here Mockito.any() returns a type inferred by the target of the invocation, that is executorService.invokeAll() . But executorService.invokeAll() doesn't declare a more specific type either as it also relies on the target inference : Mockito.when(...) .

The type returned by any() depends on the type returned by invokeAll() that depends on the type returned by when() .

But neither invokeAll() nor when() specifies the A type in the generic collection.

So the generic T type of the invokeAll() method defined as :

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)

will be seen by the compiler as Object such as :

<Object> List<Future<Object>> invokeAll(Collection<? extends Callable<Object>> tasks)

And so any() will return a Collection<? extends Callable<Object>> Collection<? extends Callable<Object>> type.

In these conditions, thenReturn() that doesn't return a List<Future<Object>> but a List<Future<A>> declared type cannot compile :

 argument mismatch; List<Future<A>> cannot be converted to List<Future<Object>>) 

To solve your issue, you need to do an unchecked cast on Mockito.any() (but you know that it will work):

Mockito.when(executorService.invokeAll((Collection<Callable<A>>)Mockito.any())).thenReturn(list);

Or as alternative, solve the target inference by setting explicitly the return of Mockito.any() in a variable :

Collection<Callable<A>> any = Mockito.any();
Mockito.when(executorService.invokeAll(any)).thenReturn(list);

You should have more success with:

    ExecutorService executorService = mock(ExecutorService.class);
    Future<Object> mockedFuture = mock(Future.class);
    List<Future<Object>> list = Lists.newArrayList(mockedFuture);

    when(executorService.invokeAll(any())).thenReturn(list);

There's a clue for this the error message:

argument mismatch; List<Future<A>> cannot be converted to List<Future<Object>>

That's because with Java generics, List<Future<A> is not a List<Future<Object>> (nor is Future<A> a Future<Object> ).

There are many good explanations for why this is the case, have a look on SO for Java contravariance.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM