Mockito isA(Class<T> clazz) How to resolve type safety?

Mike picture Mike · Aug 17, 2012 · Viewed 19.1k times · Source

in my test I have the following line:

when(client.runTask(anyString(), anyString(), isA(Iterable.class)).thenReturn(...)

isA(Iterable.class) produces warning that it needs unchecked conversion to conform to Iterable<Integer> . What is syntax for that?

isA(Iterable<Integer>.class)
isA((Iterable<Integer>)Iterable.class

do not work.

Any suggestions?

Answer

Arend v. Reinersdorff picture Arend v. Reinersdorff · Aug 18, 2012

Mockito/Hamcrest and generic classes

Yes, this is a general problem with Mockito/Hamcrest. Generally using isA() with generic classes produces a warning.

There are predifined Mockito matchers for the most common generic classes: anyList(), anyMap(), anySet() and anyCollection().

Suggestions:

anyIterable() in Mockito 2.1.0

Mockito 2.1.0 added a new anyIterable() method for matching Iterables:

when(client.runTask(anyString(), anyString(), anyIterable()).thenReturn(...)

Ignore in Eclipse

If you just want to get rid of the warning in Eclipse. Option exists since Eclipse Indigo:

Window > Preferences > Java > Compiler > Errors/Warnings > Generic types > Ignore unavoidable generic type problems

Quick Fix with @SuppressWarnings

I suggest you do this if you have the problem only once. I personally don't remember ever needing an isA(Iterable.class).

As Daniel Pryden says, you can limit the @SuppressWarnings to a local variable or a helper method.

Use a generic isA() matcher with TypeToken

This solves the problem for good. But it has two disadvantages:

  • The syntax is not too pretty and might confuse some people.
  • You have an additional dependency on the library providing the TypeToken class. Here I used the TypeToken class from Guava. There's also a TypeToken class in Gson and a GenericType in JAX-RS.

Using the generic matcher:

import static com.arendvr.matchers.InstanceOfGeneric.isA;
import static org.mockito.ArgumentMatchers.argThat;

// ...

when(client.runTask(anyString(), anyString(), argThat(isA(new TypeToken<Iterable<Integer>>() {}))))
            .thenReturn(...);

Generic matcher class:

package com.arendvr.matchers;

import com.google.common.reflect.TypeToken;
import org.mockito.ArgumentMatcher;

public class InstanceOfGeneric<T> implements ArgumentMatcher<T> {
    private final TypeToken<T> typeToken;

    private InstanceOfGeneric(TypeToken<T> typeToken) {
        this.typeToken = typeToken;
    }

    public static <T> InstanceOfGeneric<T> isA(TypeToken<T> typeToken) {
        return new InstanceOfGeneric<>(typeToken);
    }

    @Override
    public boolean matches(Object item) {
        return item != null && typeToken.getRawType().isAssignableFrom(item.getClass());
    }
}