Mockito coincide con cualquier argumento de clase

153

¿Hay alguna manera de hacer coincidir algún argumento de clase de la rutina de muestra a continuación?

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

¿Cómo puedo devolver siempre un new B()independientemente de en qué clase se pasa method? El siguiente intento solo funciona para el caso específico donde Acoincide.

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

EDITAR : Una solución es

(Class<?>) any(Class.class)
Johan Sjöberg
fuente
66
Class<?>¡asombroso!
António Almeida
Su (Clase <?>) Cualquier solución (Clase.clase) debería ser la respuesta aquí. Prefiero usar eso que la clase ClassOrSubclassMatcher que se ve a continuación.
superbAfterSemperPhi
@superbAfterSemperPhi y johan-sjöberg Publiqué otra forma de hacerlo, sin elenco. Creo que podría ser una mejor manera. ¿Qué piensas?
anmaia

Respuestas:

188

Dos formas más de hacerlo (vea mi comentario sobre la respuesta anterior de @Tomasz Nurkiewicz):

El primero se basa en el hecho de que el compilador simplemente no le permitirá pasar algo del tipo incorrecto:

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

Pierde el tipeo exacto (el Class<? extends A>) pero probablemente funcione como lo necesita.

El segundo es mucho más complicado, pero podría decirse que es una mejor solución si realmente quieres estar seguro de que el argumento method()es una Ao una subclase de A:

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

Donde ClassOrSubclassMatcherse org.hamcrest.BaseMatcherdefine como:

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

¡Uf! Iría con la primera opción hasta que realmente necesites tener un control más preciso sobre lo que method()realmente regresa :-)

casa del Molino
fuente
me if (obj instanceof Class)arruina las cosas, así que lo eliminé.
Daniel Smith
En la línea de declaración de la clase, tuve que cambiar extends BaseMatcher<Class<T>>a justo extends BaseMatcher<T>. Solo para tu información, si alguien más obtiene errores de compilación, pruébalo.
Jan
También tuve que cambiar la matchesfunción a la siguiente:public boolean matches(Object obj) { if (obj != null) { return targetClass.isAssignableFrom(obj.getClass()); } return false; }
Jan
any (Class.class) está volviendo nulo - ¿cómo puedo evitar volver nulo?
Arvind Kumar
sería genial si realmente agrego la clase que necesito importar ya que ahora hay muchos "cualquiera" de mockito
jpganz18
53

Hay otra forma de hacerlo sin yeso:

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

Esta solución obliga al método any()a devolver el Class<A>tipo y no su valor predeterminado ( Object).

anmaia
fuente
55
Matchersestá en desuso en las versiones más recientes de Mockito y probablemente se eliminará en la versión 3.0. Utilice en su ArgumentMatcherslugar:when(a.method(ArgumentMatchers.<Class<A>>any())).thenReturn(b);
Voicu
41

Si no tiene idea de qué paquete necesita importar:

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

O

import org.mockito.ArgumentMatchers;
ArgumentMatchers.any(SomeClass.class)
Joao Luiz Cadore
fuente
13
Esto me salvó la vida, estaba importando accidentalmente "cualquiera" de la biblioteca Hamcrest.
Gábor Nagy
3
Ahora ha cambiado aorg.mockito.ArgumentMatchers.any
BOWS
27

Qué tal si:

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

o:

when(a.method((A)notNull())).thenReturn(b);
Tomasz Nurkiewicz
fuente
44
Estos se compilarían y funcionarían si la firma del método fuera method(A a), pero es (efectivamente) method(Class<A> a), por lo que necesitaría usar: when(a.method(isA(Class.class))).thenReturn(b);owhen(a.method((Class<A>) notNull())).thenReturn(b);
millhouse
La segunda parte para mí funciona como encanto. pelear con cualquier (SomeClass.class) conducir a un callejón sin salida. Pero (SomeClass.class) notNull () me salvó el día
Vadim
Si tiene dos métodos con el mismo nombre pero con argumentos diferentes, puede desambiguar el método que se burlará usando la segunda versión aquí. La primera versión no fue suficiente para mí (en Java 8).
Patru
Gracias, isA (A.class) funciona bien para mí y el mvcConversionService selecciona la clase correcta. Esto no funcionaba con ninguna (A.class) y eq (A.class).
d3rbastl3r
9

la solución de millhouse ya no funciona con la versión reciente de mockito

Esta solución funciona con Java 8 y Mockito 2.2.9

donde ArgumentMatcheres una instancia 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;
    }
}

Y el uso

when(a.method(ArgumentMatchers.argThat(new ClassOrSubclassMatcher<>(A.class)))).thenReturn(b);
Bertrand Cedric
fuente
la instancia de condición ya no es necesaria, y escribí un método de conveniencia:public static <T> Class<T> subClassOf(Class<T> targetClass) { return argThat(new ClassOrSubclassMatcher<>(targetClass)); }
Daniel Alder