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)

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


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


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)

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

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.