¿Puede Mockito tropezar un método sin tener en cuenta el argumento?

302

Estoy tratando de probar un código heredado, usando Mockito.

Quiero tropezar con uno FooDaoque se usa en producción de la siguiente manera:

foo = fooDao.getBar(new Bazoo());

Puedo escribir:

when(fooDao.getBar(new Bazoo())).thenReturn(myFoo);

Pero el problema obvio es que getBar()nunca se llama con el mismo Bazooobjeto para el que calculé el método. (¡Maldición a ese newoperador!)

Me encantaría si pudiera tropezar el método de una manera que regrese myFooindependientemente del argumento. De lo contrario, escucharé otras sugerencias de solución, pero realmente me gustaría evitar cambiar el código de producción hasta que haya una cobertura de prueba razonable.

Eric Wilson
fuente

Respuestas:

456
when(
  fooDao.getBar(
    any(Bazoo.class)
  )
).thenReturn(myFoo);

o (para evitar nulls):

when(
  fooDao.getBar(
    (Bazoo)notNull()
  )
).thenReturn(myFoo);

No olvides importar matchers (hay muchos otros disponibles):

Para Mockito 2.1.0 y más reciente:

import static org.mockito.ArgumentMatchers.*;

Para versiones anteriores:

import static org.mockito.Matchers.*;
Tomasz Nurkiewicz
fuente
2
Me encanta cuando la respuesta precede al final de 'aceptar respuesta congelada'.
Eric Wilson
10
Hay algo notNull(Bazoo.class)así como any(Bazoo.class)(tal vez no existía en el momento de esta respuesta)
Dandre Allison
2
que tenía una situación un poco especial donde podría tener cualquiera de los dos argumentos posibles - Bazooo Cazoolos que son ambas subclases de, por ejemplo, Azoo. porque Bazoonecesitaba regresar foo, pero porque Cazoonecesitaba regresar bar. En esta situación, la Matchers.any()solución propuesta no funciona, sin embargo, Matchers.isA()funciona perfectamente.
Tanvir
3
org.mockito.Matchersahora está en desuso - use org.mockito.ArgumentMatchersen su lugar, es decir import static org.mockito.ArgumentMatchers.*(ver documentos )
DontDivideByZero
when(myFoo.knowsWhatsUp()).thenReturn(myMoney);
6rchid
18

Usar así:

when(
  fooDao.getBar(
    Matchers.<Bazoo>any()
  )
).thenReturn(myFoo);

Antes de que necesites importar Mockito.Matchers

Hamad
fuente
1
Esto es depricated!
DrB
15

http://site.mockito.org/mockito/docs/1.10.19/org/mockito/Matchers.html

anyObject() debe ajustarse a tus necesidades.

Además, siempre puede considerar implementar hashCode()y equals()para la Bazooclase. Esto haría que su ejemplo de código funcione de la manera que desee.

Buhb
fuente
Estoy de acuerdo con la segunda sugerencia, pero sigo optando por no hacerlo por razones no técnicas.
Eric Wilson
1
La clase Matchers está en desuso (ver documentos - "Esta clase probablemente se eliminará en la versión 3.0" )
Johannes Rabauer
1

Otra opción es confiar en un buen equalsmétodo antiguo . Mientras el argumento en el whensimulacro sea equalsel argumento en el código que se está probando, entonces Mockito coincidirá con el simulacro.

Aquí hay un ejemplo.

public class MyPojo {

    public MyPojo( String someField ) {
        this.someField = someField;
    }

    private String someField;

    @Override
    public boolean equals( Object o ) {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
        MyPojo myPojo = ( MyPojo ) o;
        return someField.equals( myPojo.someField );
    }

}

luego, suponiendo que sepa cuál someFieldserá el valor , puede burlarse de esta manera.

when(fooDao.getBar(new MyPojo(expectedSomeField))).thenReturn(myFoo);

Pros: esto es más explícito que los anycomparadores. Como revisor de código, mantengo un ojo abierto anyen el código que escriben los desarrolladores junior, ya que echa un vistazo a la lógica de su código para generar el objeto apropiado que se pasa.

con: a veces el campo que se pasa al objeto es una ID aleatoria. Para este caso, no puede construir fácilmente el objeto de argumento esperado en su código simulado.

Otro enfoque posible es usar el Answerobjeto de Mockito que se puede usar con el whenmétodo. Answerle permite interceptar la llamada real e inspeccionar el argumento de entrada y devolver un objeto simulado. En el siguiente ejemplo, estoy usando anypara capturar cualquier solicitud del método que se está burlando. Pero luego, en la Answerlambda, puedo inspeccionar más el argumento de Bazo ... tal vez para verificar que se le haya pasado una identificación adecuada. Prefiero esto anypor sí solo para que al menos se realice una inspección sobre el argumento.

    Bar mockBar = //generate mock Bar.

    when(fooDao.getBar(any(Bazo.class))
    .thenAnswer(  ( InvocationOnMock invocationOnMock) -> {
        Bazo actualBazo = invocationOnMock.getArgument( 0 );

        //inspect the actualBazo here and thrw exception if it does not meet your testing requirements.
        return mockBar;
    } );

Entonces, para resumir todo, me gusta confiar en equals(donde el argumento esperado y el argumento real deberían ser iguales entre sí) y si igual no es posible (debido a que no puedo predecir el estado del argumento real), recurriré para Answerinspeccionar el argumento.

Jose Martinez
fuente