Mockito passt zu jedem Klassenargument

153

Gibt es eine Möglichkeit, mit einem Klassenargument der folgenden Beispielroutine übereinzustimmen?

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

Wie kann ich immer eine zurückgeben, new B()unabhängig davon, an welche Klasse übergeben wird method? Der folgende Versuch funktioniert nur für den speziellen Fall, in dem Aeine Übereinstimmung vorliegt.

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

EDIT : Eine Lösung ist

(Class<?>) any(Class.class)
Johan Sjöberg
quelle
6
Class<?>tolle!
António Almeida
Ihre (Klasse <?>) Beliebige (Klasse.Klasse) Lösung sollte hier die Antwort sein. Ich würde das viel lieber verwenden als die unten gezeigte ClassOrSubclassMatcher-Klasse.
SuperbAfterSemperPhi
@superbAfterSemperPhi und johan-sjöberg Ich habe einen anderen Weg gepostet, dies ohne Besetzung zu tun. Ich glaube, das könnte ein besserer Weg sein. Was denken Sie?
Anaia

Antworten:

188

Zwei weitere Möglichkeiten (siehe meinen Kommentar zur vorherigen Antwort von @Tomasz Nurkiewicz):

Der erste beruht auf der Tatsache, dass der Compiler Sie einfach nicht zulässt, dass etwas vom falschen Typ übergeben wird:

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

Sie verlieren die genaue Eingabe (die Class<? extends A>), aber es funktioniert wahrscheinlich so, wie Sie es brauchen.

Die zweite ist viel komplizierter, aber wahrscheinlich eine bessere Lösung, wenn Sie wirklich sicher sein möchten, dass das Argument method()eine Aoder eine Unterklasse von A:

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

Wo ClassOrSubclassMatcherist ein org.hamcrest.BaseMatcherdefiniert als:

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

Puh! Ich würde mit der ersten Option gehen, bis Sie wirklich eine genauere Kontrolle darüber bekommen müssen, was method()tatsächlich zurückkommt :-)

Mühle
quelle
Das if (obj instanceof Class)bringt die Dinge für mich durcheinander, also habe ich es entfernt.
Daniel Smith
In der Deklarationszeile der Klasse musste ich extends BaseMatcher<Class<T>>zu just wechseln extends BaseMatcher<T>. Nur zu Ihrer Information, wenn jemand andere Kompilierungsfehler bekommt, probieren Sie das aus.
Januar
Ich musste auch die ändern matchesFunktion auf die folgenden:public boolean matches(Object obj) { if (obj != null) { return targetClass.isAssignableFrom(obj.getClass()); } return false; }
Jan
any (Class.class) gibt null zurück - wie kann ich vermeiden, null zurückzugeben
Arvind Kumar
wäre toll, wenn ich tatsächlich die Klasse hinzufügen würde, die ich importieren muss, da jetzt viele "beliebige" von mockito
jpganz18
53

Es gibt einen anderen Weg, dies ohne Besetzung zu tun:

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

Diese Lösung zwingt die Methode any(), den Class<A>Typ und nicht den Standardwert ( Object) zurückzugeben.

anmaia
quelle
5
Matchersist in neueren Versionen von Mockito veraltet und wird wahrscheinlich in Version 3.0 entfernt. Verwenden Sie ArgumentMatchersstattdessen:when(a.method(ArgumentMatchers.<Class<A>>any())).thenReturn(b);
Voicu
40

Wenn Sie keine Ahnung haben, welches Paket Sie importieren müssen:

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

ODER

import org.mockito.ArgumentMatchers;
ArgumentMatchers.any(SomeClass.class)
Joao Luiz Cadore
quelle
12
Dies hat mein Leben gerettet, ich habe versehentlich "any" aus der Hamcrest-Bibliothek importiert.
Gábor Nagy
3
Jetzt hat es sich geändert zuorg.mockito.ArgumentMatchers.any
BOWS
27

Wie wäre es mit:

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

oder:

when(a.method((A)notNull())).thenReturn(b);
Tomasz Nurkiewicz
quelle
4
Diese würden kompiliert und funktionieren, wenn die Methodensignatur method(A a)- aber es ist (effektiv) method(Class<A> a)- wäre, also müssten Sie verwenden: when(a.method(isA(Class.class))).thenReturn(b);oderwhen(a.method((Class<A>) notNull())).thenReturn(b);
Millhouse
Der zweite Teil wirkt für mich wie ein Zauber. Kämpfe mit irgendwelchen (SomeClass.class) führen zu einer Sackgasse. Aber (SomeClass.class) notNull () hat meinen Tag gerettet
Vadim
Wenn Sie zwei Methoden mit demselben Namen, aber unterschiedlichen Argumenten haben, können Sie die zu verspottende Methode mit der zweiten Version hier eindeutig unterscheiden. Die erste Version hat es für mich nicht geschnitten (auf Java 8 also).
Patru
Danke, isA (A.class) funktioniert für mich einwandfrei und der mvcConversionService wählt die richtige Klasse aus. Dies funktionierte nicht mit irgendwelchen (A.class) und eq (A.class).
d3rbastl3r
9

Die Lösung von Millhouse funktioniert mit der neuesten Version von Mockito nicht mehr

Diese Lösung funktioniert mit Java 8 und Mockito 2.2.9

wo ArgumentMatcherist eine Instanz vonorg.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;
    }
}

Und die Verwendung

when(a.method(ArgumentMatchers.argThat(new ClassOrSubclassMatcher<>(A.class)))).thenReturn(b);
Bertrand Cedric
quelle
Die Bedingung ist nicht mehr erforderlich, und ich habe eine Convenience-Methode geschrieben:public static <T> Class<T> subClassOf(Class<T> targetClass) { return argThat(new ClassOrSubclassMatcher<>(targetClass)); }
Daniel Alder