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.