205

Is there a way to match any class argument of the below sample routine?

class A {
     public B method(Class<? extends A> a) {}
}

How can I always return a new B() regardless of which class is passed into method? The following attempt only works for the specific case where A is matched.

A a = new A();
B b = new B();
when(a.method(eq(A.class))).thenReturn(b);

EDIT: One solution is

(Class<?>) any(Class.class)
Johan Sjöberg
  • 47,929
  • 21
  • 130
  • 148
  • Your (Class>) any(Class.class) solution should be the answer here. I'd much rather use that then the ClassOrSubclassMatcher class seen below. – superbAfterSemperPhi May 26 '15 at 17:12
  • @superbAfterSemperPhi and johan-sjöberg I posted another way to do that, without cast. I believe that could be a better way. What do you think? – anmaia Jan 13 '16 at 18:37

6 Answers6

235

Two more ways to do it (see my comment on the previous answer by @Tomasz Nurkiewicz):

The first relies on the fact that the compiler simply won't let you pass in something of the wrong type:

when(a.method(any(Class.class))).thenReturn(b);

You lose the exact typing (the Class<? extends A>) but it probably works as you need it to.

The second is a lot more involved but is arguably a better solution if you really want to be sure that the argument to method() is an A or a subclass of A:

when(a.method(Matchers.argThat(new ClassOrSubclassMatcher<A>(A.class)))).thenReturn(b);

Where ClassOrSubclassMatcher is an org.hamcrest.BaseMatcher defined as:

public class ClassOrSubclassMatcher<T> extends BaseMatcher<Class<T>> {

    private final Class<T> targetClass;

    public ClassOrSubclassMatcher(Class<T> targetClass) {
        this.targetClass = targetClass;
    }

    @SuppressWarnings("unchecked")
    public boolean matches(Object obj) {
        if (obj != null) {
            if (obj instanceof Class) {
                return targetClass.isAssignableFrom((Class<T>) obj);
            }
        }
        return false;
    }

    public void describeTo(Description desc) {
        desc.appendText("Matches a class or subclass");
    }       
}

Phew! I'd go with the first option until you really need to get finer control over what method() actually returns :-)

millhouse
  • 9,817
  • 4
  • 32
  • 40
  • the `if (obj instanceof Class)` messes things up for me, so I removed it. – Daniel Smith Oct 24 '14 at 04:22
  • On the declaration line of the class, I had to change `extends BaseMatcher>` to just `extends BaseMatcher`. Just FYI, if anyone else gets compile errors, try that out. – Jan Jul 26 '17 at 21:09
  • I also had to change the `matches` function to the following: ```public boolean matches(Object obj) { if (obj != null) { return targetClass.isAssignableFrom(obj.getClass()); } return false; }``` – Jan Jul 26 '17 at 21:13
  • any(Class.class) is returning null - how can i avoid to return null – Arvind Kumar Aug 23 '19 at 11:35
  • would be great if actually add the class that I need to import since now are many "any" from mockito – jpganz18 Sep 02 '19 at 13:05
79

There is another way to do that without cast:

when(a.method(Matchers.<Class<A>>any())).thenReturn(b);

This solution forces the method any() to return Class<A> type and not its default value (Object).

anmaia
  • 948
  • 6
  • 8
  • 21
    `Matchers` is deprecated in newer versions of Mockito and will likely be removed in version 3.0. Use `ArgumentMatchers` instead: `when(a.method(ArgumentMatchers.>any())).thenReturn(b);` – Voicu Sep 20 '18 at 23:19
  • Thanks, worked perfectly! – Gustavo Amaro Feb 11 '22 at 16:53
  • 1
    Perfect, the Matchers.>any() is the special sauce here! It lets you match a class of this type. – Jason D Jun 10 '22 at 14:47
64

If you have no idea which Package you need to import:

import static org.mockito.ArgumentMatchers.any;
any(SomeClass.class)

OR

import org.mockito.ArgumentMatchers;
ArgumentMatchers.any(SomeClass.class)
8bitjunkie
  • 12,793
  • 9
  • 57
  • 70
Joao Luiz Cadore
  • 2,656
  • 1
  • 15
  • 11
30

How about:

when(a.method(isA(A.class))).thenReturn(b);

or:

when(a.method((A)notNull())).thenReturn(b);
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
12

the solution from millhouse is not working anymore with recent version of mockito

This solution work with java 8 and mockito 2.2.9

where ArgumentMatcher is an instanceof org.mockito.ArgumentMatcher

public class ClassOrSubclassMatcher<T> implements ArgumentMatcher<Class<T>> {

   private final Class<T> targetClass;

    public ClassOrSubclassMatcher(Class<T> targetClass) {
        this.targetClass = targetClass;
    }

    @Override
    public boolean matches(Class<T> obj) {
        if (obj != null) {
            if (obj instanceof Class) {
                return targetClass.isAssignableFrom( obj);
            }
        }
        return false;
    }
}

And the use

when(a.method(ArgumentMatchers.argThat(new ClassOrSubclassMatcher<>(A.class)))).thenReturn(b);
Bertrand Cedric
  • 653
  • 6
  • 11
  • the instanceof condition is not necessary anymore, and I wrote a convenience method: `public static Class subClassOf(Class targetClass) { return argThat(new ClassOrSubclassMatcher<>(targetClass)); }` – Daniel Alder Feb 03 '20 at 13:48
1

None of the examples above worked for me, as I'm required to mock one method multiple times for different class type parameters.

Instead, this works.

//Handle InstrumentType.class
Mockito.doReturn(new InstrumentTypeMapper() {
    @Override
    public InstrumentType map(String sourceType) throws Exception {
        return InstrumentType.Unknown;
    }
}).when(mappingLoader).load(any(ServiceCode.class), argThat(new ArgumentMatcher<Class<InstrumentType>>() {
    @Override
    public boolean matches(Class<InstrumentType> argument) {
        return InstrumentType.class.isAssignableFrom(argument);
    }
}));

//Handle InstrumentSubType.class    
Mockito.doReturn(new InstrumentSubTypeMapper() {
    @Override
    public InstrumentSubType map(String sourceType) throws Exception {
        return InstrumentSubType.istNone;
    }
}).when(mappingLoader).load(any(ServiceCode.class), argThat(new ArgumentMatcher<Class<InstrumentSubType>>() {
    @Override
    public boolean matches(Class<InstrumentSubType> argument) {
        return InstrumentSubType.class.isAssignableFrom(argument);
    }
}));

This is the short version:

Mockito.doReturn(new InstrumentTypeMapper() {
    @Override
    public InstrumentType map(String sourceType) throws Exception {
        return InstrumentType.Unknown;
    }
}).when(mappingLoader).load(any(ServiceCode.class), argThat((ArgumentMatcher<Class<InstrumentType>>) InstrumentType.class::isAssignableFrom));

Mockito.doReturn(new InstrumentSubTypeMapper() {
    @Override
    public InstrumentSubType map(String sourceType) throws Exception {
        return InstrumentSubType.istNone;
    }
}).when(mappingLoader).load(any(ServiceCode.class), argThat((ArgumentMatcher<Class<InstrumentSubType>>) InstrumentSubType.class::isAssignableFrom));

As you can see, I'm using custom ArgumentMatchers together with argThat, not sure if there is a shorter way that also works.

Marian Klühspies
  • 15,824
  • 16
  • 93
  • 136