Mockito соответствует любому аргументу класса

153

Есть ли способ сопоставить какой-либо аргумент класса из приведенной ниже примерной процедуры?

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

Как я всегда могу вернуть new B()независимо от того, в какой класс передается method? Следующая попытка работает только для конкретного случая, где Aсопоставляется.

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

РЕДАКТИРОВАТЬ : одно решение

(Class<?>) any(Class.class)
Йохан Шёберг
источник
6
Class<?>удивительный!
Антонио Алмейда
Ваше (Class <?>) Любое (Class.class) решение должно быть ответом здесь. Я бы предпочел использовать его тогда, чем класс ClassOrSubclassMatcher, показанный ниже.
superbAfterSemperPhi
@superbAfterSemperPhi и johan-sjöberg Я опубликовал еще один способ сделать это без приведения. Я считаю, что это может быть лучше. Что вы думаете?
anmaia

Ответы:

188

Еще два способа сделать это (см. Мой комментарий к предыдущему ответу @Tomasz Nurkiewicz):

Первый основан на том факте, что компилятор просто не позволит вам передать что-то неправильного типа:

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

Вы теряете точное печатание (the Class<? extends A>), но, вероятно, оно работает так, как вам нужно.

Второй вариант гораздо сложнее, но, возможно, это лучшее решение, если вы действительно хотите быть уверены, что аргументом method()является Aили является подклассом A:

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

Там , где ClassOrSubclassMatcherприведен org.hamcrest.BaseMatcherопределяется как:

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");
    }       
}

Уф! Я бы выбрал первый вариант, пока вам действительно не нужно получить более точный контроль над тем, что на method()самом деле возвращает :-)

Millhouse
источник
что if (obj instanceof Class)портит вещи для меня, так что я удалил его.
Даниэль Смит
В строке объявления класса мне пришлось изменить extends BaseMatcher<Class<T>>на просто extends BaseMatcher<T>. Просто к сведению, если кто-то еще получит ошибки компиляции, попробуйте это.
Янв
Я также должен был изменить matchesфункцию на следующее:public boolean matches(Object obj) { if (obj != null) { return targetClass.isAssignableFrom(obj.getClass()); } return false; }
Янв
any (Class.class) возвращает ноль - как я могу избежать возврата
Арвинд Кумар
было бы здорово, если бы на самом деле добавили класс, который мне нужно импортировать, так как сейчас много «любых» из mockito
jpganz18
53

Есть другой способ сделать это без приведения:

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

Это решение заставляет метод any()возвращать Class<A>тип, а не его значение по умолчанию ( Object).

anmaia
источник
5
Matchersустарела в новых версиях Mockito и, вероятно, будет удалена в версии 3.0. Используйте ArgumentMatchersвместо этого:when(a.method(ArgumentMatchers.<Class<A>>any())).thenReturn(b);
Voicu
42

Если вы не знаете, какой пакет вам нужно импортировать:

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

ИЛИ

import org.mockito.ArgumentMatchers;
ArgumentMatchers.any(SomeClass.class)
Жоао Луис Кадоре
источник
13
Это спасло мне жизнь, я случайно импортировал "any" из библиотеки Hamcrest.
Габор Надь
3
Теперь он изменился наorg.mockito.ArgumentMatchers.any
BOWS
27

Как насчет:

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

или:

when(a.method((A)notNull())).thenReturn(b);
Томаш Нуркевич
источник
4
Они скомпилировались бы и работали бы, если бы подпись метода была method(A a)- но это (эффективно) method(Class<A> a)- так что вам нужно было бы использовать: when(a.method(isA(Class.class))).thenReturn(b);илиwhen(a.method((Class<A>) notNull())).thenReturn(b);
millhouse
Вторая часть для меня работает как шарм. борьба с любым (SomeClass.class) ведет в тупик. Но (SomeClass.class) notNull () спас мой день
Вадим
Если у вас есть два метода с одним и тем же именем, но разными аргументами, вы можете устранить неоднозначность метода, который нужно смоделировать, используя вторую версию здесь. Первая версия не решила его для меня (на Java 8, то есть).
Патру
Спасибо, isA (A.class) работает для меня просто отлично, а mvcConversionService выбирает правильный класс. Это не работает ни с одним (A.class) и eq (A.class).
d3rbastl3r
9

Решение от Millhouse больше не работает с последней версией Mockito

Это решение работает с Java 8 и Mockito 2.2.9

где ArgumentMatcherэкземпляр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;
    }
}

И использование

when(a.method(ArgumentMatchers.argThat(new ClassOrSubclassMatcher<>(A.class)))).thenReturn(b);
Бертран Седрик
источник
условие instanceof больше не нужно, и я написал удобный метод:public static <T> Class<T> subClassOf(Class<T> targetClass) { return argThat(new ClassOrSubclassMatcher<>(targetClass)); }
Даниэль Алдер