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)

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


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


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)

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

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

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.