Mockito match any class argument

2019-01-30 00:25发布

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)

5条回答
Deceive 欺骗
2楼-- · 2019-01-30 00:36

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).

查看更多
可以哭但决不认输i
3楼-- · 2019-01-30 00:42

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

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

OR

import org.mockito.Matchers;
Matchers.any(SomeClass.class)
查看更多
看我几分像从前
4楼-- · 2019-01-30 00:45

How about:

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

or:

when(a.method((A)notNull())).thenReturn(b);
查看更多
The star\"
5楼-- · 2019-01-30 00:46

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);
查看更多
smile是对你的礼貌
6楼-- · 2019-01-30 00:55

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 :-)

查看更多
登录 后发表回答