Mockito correspond à n'importe quel argument de classe

153

Existe-t-il un moyen de faire correspondre n'importe quel argument de classe de l'exemple de routine ci-dessous?

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

Comment puis-je toujours renvoyer un, new B()quelle que soit la classe à laquelle est passé method? La tentative suivante ne fonctionne que pour le cas spécifique où Acorrespond.

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

EDIT : Une solution est

(Class<?>) any(Class.class)
Johan Sjöberg
la source
6
Class<?>incroyable!
António Almeida
Votre solution (Class <?>) Any (Class.class) devrait être la réponse ici. Je préfère de loin utiliser cela alors la classe ClassOrSubclassMatcher vue ci-dessous.
superbAfterSemperPhi
@superbAfterSemperPhi et johan-sjöberg J'ai posté une autre façon de faire ça, sans casting. Je pense que cela pourrait être une meilleure façon. Qu'est-ce que tu penses?
anmaia

Réponses:

188

Deux autres façons de le faire (voir mon commentaire sur la réponse précédente de @Tomasz Nurkiewicz):

Le premier repose sur le fait que le compilateur ne vous laissera tout simplement pas passer quelque chose du mauvais type:

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

Vous perdez la frappe exacte (le Class<? extends A>) mais cela fonctionne probablement comme vous en avez besoin.

Le second est beaucoup plus complexe mais est sans doute une meilleure solution si vous voulez vraiment être sûr que l'argument de method()est une Aou une sous-classe de A:

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

ClassOrSubclassMatcherest org.hamcrest.BaseMatcherdéfini comme:

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

Phew! J'irais avec la première option jusqu'à ce que vous ayez vraiment besoin d'un contrôle plus fin sur ce method()qui revient réellement :-)

moulin
la source
les if (obj instanceof Class)choses gâchent pour moi, alors je l'ai enlevé.
Daniel Smith
Sur la ligne de déclaration de la classe, j'ai dû changer extends BaseMatcher<Class<T>>pour juste extends BaseMatcher<T>. Juste pour info, si quelqu'un d'autre obtient des erreurs de compilation, essayez-le.
Jan
Je devais aussi changer la matchesfonction de ce qui suit:public boolean matches(Object obj) { if (obj != null) { return targetClass.isAssignableFrom(obj.getClass()); } return false; }
Jan
any (Class.class) renvoie null - comment puis-je éviter de retourner null
Arvind Kumar
ce serait génial si réellement ajouter la classe que j'ai besoin d'importer car il y a maintenant beaucoup de "any" de mockito
jpganz18
53

Il existe une autre façon de faire cela sans lancer:

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

Cette solution force la méthode any()à renvoyer le Class<A>type et non sa valeur par défaut ( Object).

Anmaia
la source
5
Matchersest obsolète dans les nouvelles versions de Mockito et sera probablement supprimé dans la version 3.0. Utilisez à la ArgumentMatchersplace:when(a.method(ArgumentMatchers.<Class<A>>any())).thenReturn(b);
Voicu
42

Si vous ne savez pas quel package vous devez importer:

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

OU

import org.mockito.ArgumentMatchers;
ArgumentMatchers.any(SomeClass.class)
Joao Luiz Cadore
la source
13
Cela m'a sauvé la vie, j'importais accidentellement "any" de la bibliothèque hamcrest.
Gábor Nagy
3
Maintenant, il a changé pourorg.mockito.ArgumentMatchers.any
BOWS
27

Que diriez-vous:

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

ou:

when(a.method((A)notNull())).thenReturn(b);
Tomasz Nurkiewicz
la source
4
Ceux-ci compileraient et fonctionneraient si la signature de la méthode était method(A a)- mais c'est (effectivement) method(Class<A> a)- donc vous auriez besoin d'utiliser: when(a.method(isA(Class.class))).thenReturn(b);ouwhen(a.method((Class<A>) notNull())).thenReturn(b);
millhouse
la deuxième partie pour moi fonctionne comme du charme. se battre avec n'importe quel (SomeClass.class) mène à une impasse. Mais (SomeClass.class) notNull () a sauvé ma journée
Vadim
Si vous avez deux méthodes avec le même nom mais des arguments différents, vous pouvez lever l'ambiguïté sur la méthode à simuler en utilisant la deuxième version ici. La première version ne l'a pas coupé pour moi (sur Java 8 c'est-à-dire).
Patru
Merci, isA (A.class) fonctionne très bien pour moi et le mvcConversionService sélectionne la bonne classe. Cela ne fonctionnait avec aucun (A.class) et eq (A.class).
d3rbastl3r
9

la solution de Millhouse ne fonctionne plus avec la version récente de mockito

Cette solution fonctionne avec java 8 et mockito 2.2.9

ArgumentMatcherest une instance deorg.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;
    }
}

Et l'utilisation

when(a.method(ArgumentMatchers.argThat(new ClassOrSubclassMatcher<>(A.class)))).thenReturn(b);
Bertrand Cédric
la source
l'instance de condition n'est plus nécessaire, et j'ai écrit une méthode de commodité:public static <T> Class<T> subClassOf(Class<T> targetClass) { return argThat(new ClassOrSubclassMatcher<>(targetClass)); }
Daniel Alder le