Mockito match any class argument
Mia Lopez
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) 3 6 Answers
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 :-)
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).
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) 3 How about:
when(a.method(isA(A.class))).thenReturn(b);or:
when(a.method((A)notNull())).thenReturn(b); 4 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); 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.