180 votes

Mockito correspond à n'importe quel argument de classe

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 retourner un new B() quelle que soit la classe passée dans method ? La tentative suivante ne fonctionne que pour le cas spécifique où A est apparié.

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

EDIT : Une solution est

(Class<?>) any(Class.class)

9 votes

Class<?> incroyable !

0 votes

Votre solution (Class<?>) any(Class.class) devrait être la réponse ici. Je préférerais de loin utiliser cette solution plutôt que la classe ClassOrSubclassMatcher vue ci-dessous.

0 votes

@superbAfterSemperPhi et johan-sjöberg J'ai posté une autre façon de faire cela, sans casting. Je pense que cela pourrait être une meilleure façon. Qu'en pensez-vous ?

215voto

millhouse Points 3445

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

La première repose sur le fait que le compilateur ne vous laissera tout simplement pas passer quelque chose de mauvais type :

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

Vous perdez le typage exact (le Class<? extends A> ) mais cela fonctionne probablement comme vous le souhaitez.

La seconde est beaucoup plus complexe, mais c'est sans doute la meilleure solution si vous souhaitez vraiment veulent être sûrs que l'argument de method() est un A ou une sous-classe de A :

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

ClassOrSubclassMatcher est un org.hamcrest.BaseMatcher défini comme suit :

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

Ouf ! J'opterais pour la première option jusqu'à ce que vous vraiment besoin d'avoir un contrôle plus fin sur ce que method() retourne effectivement :-)

0 votes

Le site if (obj instanceof Class) me gâche la vie, alors je l'ai supprimé.

0 votes

Sur la ligne de déclaration de la classe, j'ai dû changer extends BaseMatcher<Class<T>> à juste extends BaseMatcher<T> . Juste pour info, si quelqu'un d'autre obtient des erreurs de compilation, essayez cela.

0 votes

J'ai également dû changer le matches à ce qui suit : public boolean matches(Object obj) { if (obj != null) { return targetClass.isAssignableFrom(obj.getClass()); } return false; }

67voto

anmaia Points 738

Il y a un autre moyen de le faire sans cast :

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

Cette solution force la méthode any() pour revenir Class<A> et non sa valeur par défaut ( Object ).

60voto

Joao Luiz Cadore Points 1343

Si vous n'avez aucune idée du paquet que vous devez importer :

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

OU

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

30voto

Tomasz Nurkiewicz Points 140462

Pourquoi pas :

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

ou :

when(a.method((A)notNull())).thenReturn(b);

4 votes

Ces méthodes seraient compilées et fonctionneraient si la signature de la méthode était method(A a) - mais c'est (effectivement) method(Class<A> a) - donc vous devez utiliser : when(a.method(isA(Class.class))).thenReturn(b); o when(a.method((Class<A>) notNull())).thenReturn(b);

0 votes

La deuxième partie pour moi fonctionne comme un charme. la lutte avec any(SomeClass.class) conduit à une impasse. Mais (SomeClass.class) notNull() m'a sauvé la mise.

0 votes

Si vous avez deux méthodes avec le même nom mais des arguments différents, vous pouvez désambiguïser la méthode à simuler en utilisant la deuxième version ici. La première version n'a pas fonctionné pour moi (sur Java 8).

11voto

Bertrand Cedric Points 322

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.

donde ArgumentMatcher est une instance de org.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);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X