Tengo algunos objetos simulados que probablemente pasarán un poco y podrían terminar siendo bastante complejos.

Me gustaría que Mockito envíe un registro para cada llamada realizada a un simulacro o me gustaría que falle cada vez que se realiza una llamada inesperada para poder iterar a través de esas llamadas y configurar las respuestas apropiadas.

¿Cómo puedo lograrlo?

4
user6032518 18 oct. 2017 a las 21:46

3 respuestas

La mejor respuesta

La manera más idiomática de hacerlo es con verifyNoMoreInteractions, como en Documentos de Mockito # 8:

//interactions
mock.doSomething();
mock.doSomethingUnexpected();

//verification
verify(mock).doSomething();

//following will fail because 'doSomethingUnexpected()' is unexpected
verifyNoMoreInteractions(mock);

Digo "más idiomático" arriba porque ese método tiene su propia etiqueta de advertencia, que enlaza con la publicación del blog " ¿Debería preocuparme por lo inesperado? " del creador de Mockito Szczepan Faber.

No se recomienda utilizar verifyNoMoreInteractions() en todos los métodos de prueba. verifyNoMoreInteractions() es una afirmación útil del conjunto de herramientas de prueba de interacción. Úselo solo cuando sea relevante. Abusarlo conduce a pruebas sobreespecificadas, menos mantenibles .

En resumen, debe tener una razón muy clara para verificar lo que su dependencia no está haciendo o lo que su sistema bajo prueba está no llamando , en lugar de lo que son haciendo y llamando. Puede usar verifyNoMoreInteractions para un objeto RPC, si desea evitar llamadas RPC innecesarias, pero no (por ejemplo) una calculadora sin efectos secundarios. Aún mejor es especificar sus requisitos exactos con never() o times(int) como parámetros para verify.


Dicho esto, hay dos formas aún menos idiomáticas de hacer esto:

  • Puede tomar un registro general de las llamadas realizadas con mockingDetails(Object) e iterando a través de getInvocations(). Eso debería darle reflexivamente una lista completa de las invocaciones. Me resulta difícil imaginar cómo esto sería útil en una prueba, pero podría ser útil para limpiar un sistema existente nebuloso o mal documentado.

  • Puede realizar la acción predeterminada del simulacro para lanzar una excepción, lo que significa que si alguien llama a algo que no ha tropezado, la prueba fallará de inmediato.

    // untested pseudocode
    YourObject yourObject = Mockito.mock(YourObject.class, withSettings()
        .defaultAnswer(invocation -> {
          throw new UnsupportedOperationException(invocation.toString());
        }));
    

    Claro, eso funcionaría, pero no solo estarías violando uno de los principios básicos de Mockito (los simulacros son agradables por defecto, usando La definición de EasyMock de" agradable "), pero también te obligarías a solo usar doVerb ({ {X1}}, doAnswer, etc.) porque las llamadas a when(yourObject.doAnything()) necesariamente arrojarían esa excepción antes de que la llamada a when incluso se ejecute.

    Los desarrolladores que estén familiarizados con Mockito probablemente dirían que esta cura propensa a las excepciones es peor que la enfermedad y puede ser útil solo para diagnosticar temporalmente el código heredado más enredado.

2
Jeff Bowman 18 oct. 2017 a las 21:26

Si está tratando de rastrear el flujo, puede usar Mockito verificar para verificar si se ha realizado cierta llamada.

verify(yourMockedObject).yourMethod();

También puede usar los tiempos para verificar si ciertas llamadas deben realizarse exactamente un número determinado de veces.

verify(yourMockedObject, times(4)).yourMethod();

No es una buena práctica hacer que su unidad de prueba sea compleja. Intente probar solo una pequeña unidad de su código a la vez.

0
want2learn 18 oct. 2017 a las 19:09

La mejor respuesta que encontré es configurar Mockito para que devuelva SmartNulls .

https://static.javadoc.io/org.mockito/mockito-core/2.6.9/org/mockito/Mockito.html#RETURNS_SMART_NULLS

Esta implementación puede ser útil cuando se trabaja con código heredado. Los métodos no tubulares a menudo devuelven nulo. Si su código usa el objeto devuelto por una llamada no respondida, obtendrá una NullPointerException. Esta implementación de Answer devuelve SmartNull en lugar de nulo. SmartNull da un mensaje de excepción más agradable que NPE porque señala la línea donde se llamó al método no definido. Simplemente haga clic en el seguimiento de la pila.

Puede hacerlo de manera simulada o predeterminada (puede causar problemas con otros marcos como Spring).

Manualmente

Writer writerMock = mock(Writer.class, RETURNS_SMART_NULLS);

Anotación

@Mock(answer = Answers.RETURNS_SMART_NULLS)

Establecer como predeterminada global

La clase de configuración debe estar exactamente en este paquete. Esto podría conducir a fallas extrañas con Spring.

package org.mockito.configuration;

import org.mockito.internal.stubbing.defaultanswers.ReturnsSmartNulls;
import org.mockito.stubbing.Answer;

public class MockitoConfiguration extends DefaultMockitoConfiguration {

    public Answer<Object> getDefaultAnswer() {
        return new ReturnsSmartNulls();
    }
}

Ver: https://solidsoft.wordpress.com/2012/07/02/beyond-the-mockito-refcard-part-1-a-better-error-message-on -npe-with-globally-configure-smartnull /

Tuve problemas con SpringBootRepositories y @MockBean al habilitar el valor predeterminado global:

java.lang.ClassCastException: org.mockito.codegen.Object$MockitoMock$191495750 cannot be cast to xxx.xxx.MyObject

Ejemplo de salida de error

org.junit.ComparisonFailure: expected:<[DataRecordType{id=null, name='SomeRecord', pathTemplate='SomeTemplate'}]> but was:<[SmartNull returned by this unstubbed method call on a mock: dataRecordTypeRepository bean.getById(1L);]>
1
datenhahn 30 jul. 2018 a las 15:56