1

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?

jaeyong
  • 8,951
  • 14
  • 50
  • 63

2 Answers2

2

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>> 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);
davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • Casing works for solving the compile error. But when I run the test, it gives the following runtime error: [junit] You cannot use argument matchers outside of verification or stubbing. Especially on this line: – jaeyong Jul 25 '18 at 02:33
  • Pointing this line: ---> List> anyList = any(); – jaeyong Jul 25 '18 at 02:33
1

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.

Sadiq
  • 61
  • 1
  • 2