2012-07-23 27 views
201

Estoy usando Mockito 1.9.0. Quiero burlarse del comportamiento de un único método de una clase en una prueba unitaria, por lo que tengoMockito: intentar espiar el método es llamar al método original

final MyClass myClassSpy = Mockito.spy(myInstance); 
Mockito.when(myClassSpy.method1()).thenReturn(myResults); 

El problema está, en la segunda línea, myClassSpy.method1() está consiguiendo realmente llama, lo que resulta en una excepción. La única razón por la que estoy usando mocks es para que luego, cuando se llame a myClassSpy.method1(), no se invoque el método real y se devolverá el objeto myResults.

MyClass es una interfaz y myInstance es una implementación de eso, si eso es importante.

¿Qué debo hacer para corregir este comportamiento de espionaje?

Respuesta

348

Permítanme citar the official documentation:

Gotcha importante sobre el espionaje objetos reales!

A veces es imposible usarlo cuando (Objeto) para espiar espías. Ejemplo:

List list = new LinkedList(); 
List spy = spy(list); 

// Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty) 
when(spy.get(0)).thenReturn("foo"); 

// You have to use doReturn() for stubbing 
doReturn("foo").when(spy).get(0); 

En su caso es algo como:

doReturn(resulstIWant).when(myClassSpy).method1(); 
+15

¿Qué pasa si utilizo este método y el original sigue SIENDO recibiendo llamadas? ¿Podría haber algún problema con los parámetros que paso? Aquí está toda la prueba: http://pastebin.com/ZieY790P El método 'send' se llama –

+9

@EvgeniPetrov si su método original todavía se está llamando es probable porque su método original es definitivo. Mockito no se burla de los métodos finales y no puede advertirle sobre la burla de los métodos finales. – MarcG

+0

¿Esto también es posible para doThrow()? – Gobliins

19

Mi caso era diferente de la respuesta aceptada. Yo estaba tratando de burlarse de un método paquete-privada para una instancia que no viven en ese paquete

package common; 

public class Animal { 
    void packageProtected(); 
} 

package instances; 

class Dog extends Animal { } 

y la prueba de las clases

package common; 

public abstract class AnimalTest<T extends Animal> { 
    @Before 
    setup(){ 
    doNothing().when(getInstance()).packageProtected(); 
    } 

    abstract T getInstance(); 
} 

package instances; 

class DogTest extends AnimalTest<Dog> { 
    Dog getInstance(){ 
    return spy(new Dog()); 
    } 

    @Test 
    public void myTest(){} 
} 

La compilación es correcta, pero cuando se trata de la configuración del prueba, invoca el método real en su lugar.

Declarar el método protegida o públicas corrige el problema, aunque no es una solución limpia.

+0

Encontré un problema similar, pero la prueba y el método privado del paquete estaban en el mismo paquete. Creo que tal vez Mockito tenga problemas con los métodos privados del paquete en general. – Dave

7

¡Parece que la respuesta de Tomasz Nurkiewicz no cuenta toda la historia!

NB Mockito version: 1.10.19.

Soy mucho más un Mockito newb, por lo que no puedo explicar el siguiente comportamiento: si hay un experto que puede mejorar esta respuesta, por favor siéntase libre.

El método en cuestión aquí, getContentStringValue, es NOfinal y NOstatic.

Esta línea hace llamar al método original getContentStringValue:

doReturn("dummy").when(im).getContentStringValue(anyInt(), isA(ScoreDoc.class)); 

Esta línea no se llamada al método original getContentStringValue:

doReturn("dummy").when(im).getContentStringValue(anyInt(), any(ScoreDoc.class)); 

Por razones que no puedo responder, el uso de isA() hace que el comportamiento (?) "no llame al método" previsto de doReturn falle.

Echemos un vistazo a las firmas de métodos implicados aquí: ambos son static métodos de Matchers. El Javadoc dice que ambos devuelven null, lo cual es un poco difícil de entender. Presumiblemente, el objeto Class pasado como parámetro se examina, pero el resultado nunca se calcula o se descarta. Dado que null puede representar cualquier clase y que espera que no se llame al método falso, ¿no podrían las firmas de isA(...) y any(...) simplemente devolver null en lugar de un parámetro genérico * <T>?

De todos modos:

public static <T> T isA(java.lang.Class<T> clazz) 

public static <T> T any(java.lang.Class<T> clazz) 

documentación La API no da ninguna pista sobre esto. También parece decir que la necesidad de tal comportamiento de "no llamar método" es "muy raro". Personalmente utilizo esta técnica todo el tiempo: normalmente me parece que la burla implica unas pocas líneas que "configuran la escena" ... seguido de llamar a un método que luego "reproduce" la escena en el contexto simulado que has escenificado ... y mientras configuras el escenario y los accesorios, lo último que quieres es que los actores entren al escenario de la izquierda y comiencen a actuar con el corazón ...

Pero esto es mucho más allá de mi nivel salarial ... . Invito a las explicaciones de cualquier sumo sacerdote Mockito que pase ...

* es "parámetro genérico" el término correcto?

+0

No sé si esto agrega claridad o confunde aún más el asunto, pero la diferencia entre isA() y any() es que isA en realidad hace una comprobación de tipos, mientras que any() la familia de métodos se creó simplemente para evitar la conversión de tipos de el argumento. –

+0

@KevinWelker Gracias. Y, de hecho, los nombres de los métodos no carecen de una cierta calidad autoexplicativa. Sin embargo, lo hago, y sin importar cuán levemente, discrepe con los genios diseñadores de Mockito por no documentar adecuadamente. Sin duda, necesito leer otro libro más sobre Mockito. PD en realidad parece que hay muy pocos recursos para enseñar "intermedio Mockito"! –

+1

La historia es que los métodos anyXX se crearon primero como una forma de tratar con el encasillamiento solamente. Luego, cuando se sugirió que agreguen la verificación de los argumentos, no quisieron romper a los usuarios de la API existente, por lo que crearon la familia isA(). Sabiendo que los métodos any() deberían haber hecho la verificación de tipo todo el tiempo, retrasaron el cambio hasta que introdujeron otros cambios de última hora en la revisión de Mockito 2.X (que aún no he probado). En los métodos 2.x +, anyX() son alias para los métodos isA(). –

1

En mi caso, usando Mockito 2.0, tuve que cambiar todos los parámetros any() a nullable() para conectar la llamada real.

+0

No dejes que los 321 voten por la respuesta superior te desanime, esto resolvió mi problema :) ¡He estado luchando con esto por un par de horas! –

0

He encontrado otra razón para que el espía llame al método original.

Alguien tuvo la idea de burlarse de una clase final, y se encontró sobre MockMaker:

Como esto funciona de forma diferente a nuestro mecanismo actual y éste tiene diferentes limitaciones y como queremos recoger la experiencia y la retroalimentación de los usuarios, esta característica debe estar explícitamente activada para estar disponible; se puede hacer a través del mecanismo de extensión Mockito creando el archivo src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker que contiene una sola línea: mock-maker-inline

Fuente: https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods

Después de la combinación y el traje ese archivo a mi máquina, mis pruebas fallidas.

Solo tuve que quitar la línea (o el archivo), y spy() funcionó.

Cuestiones relacionadas