Mockito pasuje do dowolnego argumentu klasy

153

Czy istnieje sposób dopasowania dowolnego argumentu klasy w poniższej przykładowej procedurze?

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

Jak zawsze mogę zwrócić, new B()niezależnie od tego, do której klasy jest przekazywana method? Poniższa próba działa tylko w konkretnym przypadku, w którym Ajest dopasowany.

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

EDYCJA : jednym rozwiązaniem jest

(Class<?>) any(Class.class)
Johan Sjöberg
źródło
6
Class<?>niesamowity!
António Almeida
Twoje (Class <?>) Dowolne (Class.class) rozwiązanie powinno być tutaj odpowiedzią. Wolałbym raczej użyć tego niż klasy ClassOrSubclassMatcher widocznej poniżej.
superbAfterSemperPhi
@superbAfterSemperPhi i johan-sjöberg Opublikowałem inny sposób, aby to zrobić, bez obsady. Uważam, że to może być lepszy sposób. Co myślisz?
anmaia

Odpowiedzi:

188

Jeszcze dwa sposoby (zobacz mój komentarz do poprzedniej odpowiedzi autorstwa @Tomasz Nurkiewicz):

Pierwsza polega na tym, że kompilator po prostu nie pozwoli Ci przekazać czegoś niewłaściwego typu:

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

Tracisz dokładne wpisywanie (the Class<? extends A>), ale prawdopodobnie działa tak, jak tego potrzebujesz.

Drugi jest o wiele bardziej skomplikowany, ale jest prawdopodobnie lepszym rozwiązaniem, jeśli naprawdę chcesz mieć pewność, że argument do method()jest Apodklasą lub podklasą A:

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

Gdzie ClassOrSubclassMatcherjest org.hamcrest.BaseMatcherzdefiniowany jako:

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

Uff! Wybrałbym pierwszą opcję, dopóki naprawdę nie będziesz potrzebować dokładniejszej kontroli nad tym, co method()faktycznie zwraca :-)

Millhouse
źródło
że if (obj instanceof Class)mes rzeczy dla mnie, więc usunąłem go.
Daniel Smith
W linii deklaracji klasy musiałem zmienić extends BaseMatcher<Class<T>>na just extends BaseMatcher<T>. Po prostu do Twojej wiadomości, jeśli ktoś inny dostanie błędy kompilacji, wypróbuj to.
stycznia
Musiałem również zmienić matchesfunkcję na następującą:public boolean matches(Object obj) { if (obj != null) { return targetClass.isAssignableFrom(obj.getClass()); } return false; }
Sty
any (Class.class) zwraca wartość null - jak mogę uniknąć zwracania wartości null
Arvind Kumar
byłoby wspaniale, gdyby rzeczywiście dodać klasę, którą muszę zaimportować, ponieważ teraz jest wiele „dowolnych” z mockito
jpganz18
53

Jest na to inny sposób bez obsady:

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

To rozwiązanie wymusza na metodzie any()zwracanie Class<A>typu, a nie wartości domyślnej ( Object).

anmaia
źródło
5
Matchersjest przestarzałe w nowszych wersjach Mockito i prawdopodobnie zostanie usunięte w wersji 3.0. Użyj ArgumentMatcherszamiast tego:when(a.method(ArgumentMatchers.<Class<A>>any())).thenReturn(b);
Voicu
41

Jeśli nie masz pojęcia, który pakiet musisz zaimportować:

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

LUB

import org.mockito.ArgumentMatchers;
ArgumentMatchers.any(SomeClass.class)
Joao Luiz Cadore
źródło
13
To uratowało mi życie, przypadkowo importowałem „cokolwiek” z biblioteki hamcrest.
Gábor Nagy
3
Teraz zmieniło się naorg.mockito.ArgumentMatchers.any
BOWS
27

Co powiesz na:

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

lub:

when(a.method((A)notNull())).thenReturn(b);
Tomasz Nurkiewicz
źródło
4
Skompilowałyby się i działałyby, gdyby sygnatura metody była method(A a)- ale jest (efektywnie) method(Class<A> a)- więc trzeba użyć: when(a.method(isA(Class.class))).thenReturn(b);lubwhen(a.method((Class<A>) notNull())).thenReturn(b);
millhouse
druga część działa jak urok. walka z dowolną (SomeClass.class) prowadzi do ślepego zaułka. Ale (SomeClass.class) notNull () uratował mi dzień
Vadim
Jeśli masz dwie metody o tej samej nazwie, ale z różnymi argumentami, możesz ujednoznacznić metodę, która ma być wyszydzana, używając tutaj drugiej wersji. Pierwsza wersja nie zrobiła tego za mnie (to znaczy na Javie 8).
Patru
Dzięki, isA (A.class) działa dla mnie dobrze, a mvcConversionService wybiera odpowiednią klasę. To nie działało z any (A.class) i eq (A.class).
d3rbastl3r
9

rozwiązanie od millhouse nie działa już z najnowszą wersją mockito

To rozwiązanie działa z Javą 8 i Mockito 2.2.9

gdzie ArgumentMatcherjest instancjaorg.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;
    }
}

I użycie

when(a.method(ArgumentMatchers.argThat(new ClassOrSubclassMatcher<>(A.class)))).thenReturn(b);
Bertrand Cedric
źródło
warunek instancji nie jest już potrzebny i napisałem wygodną metodę:public static <T> Class<T> subClassOf(Class<T> targetClass) { return argThat(new ClassOrSubclassMatcher<>(targetClass)); }
Daniel Alder