@RunWith (MockitoJUnitRunner.class) vs MockitoAnnotations.initMocks (esto)

118

Mientras escribo una nueva prueba de jUnit4, me pregunto si debo usar @RunWith (MockitoJUnitRunner.class) o MockitoAnnotations.initMocks (esto) .

Creé una nueva prueba y el asistente generó automáticamente una prueba con el Runner. Los Javadocs para MockitoJUnitRunner indican lo siguiente:

Compatible con JUnit 4.4 y superior, este corredor agrega el siguiente comportamiento:

Inicializa simulaciones anotadas con Mock, por lo que el uso explícito de MockitoAnnotations.initMocks (Objeto) no es necesario. Los simulacros se inicializan antes de cada método de prueba. valida el uso del marco después de cada método de prueba.

No tengo claro si usar Runner tiene alguna ventaja sobre el método initMocks () que he estado usando en el pasado.

¡Cualquier comentario o enlace será apreciado!

Océano azul
fuente

Respuestas:

147

MockitoJUnitRunnerle ofrece una validación automática del uso del marco, así como un initMocks().

Realmente vale la pena tener la validación automática del uso del marco. Le brinda mejores informes si comete uno de estos errores.

  • whenLlamas al método estático , pero no completas el apéndice con una coincidencia thenReturn, thenThrowo then. (Error 1 en el código siguiente)

  • Llamas verifya un simulacro, pero te olvidas de proporcionar la llamada al método que estás intentando verificar. (Error 2 en el código siguiente)

  • Llamas al whenmétodo después doReturn, doThrowo doAnswery pasas un simulacro, pero te olvidas de proporcionar el método que estás intentando apuntar. (Error 3 en el código siguiente)

Si no tiene la validación del uso del marco, estos errores no se informan hasta la siguiente llamada a un método Mockito. Esto podría ser

  • en el mismo método de prueba (como el error 1 a continuación),
  • en el siguiente método de prueba (como el error 2 a continuación),
  • en la próxima clase de prueba.

Si ocurren en la última prueba que ejecuta (como el error 3 a continuación), no se informará en absoluto.

Así es como podría verse cada uno de esos tipos de errores. Suponga aquí que JUnit ejecuta estas pruebas en el orden en que se enumeran aquí.

@Test
public void test1() {

    // ERROR 1
    // This compiles and runs, but it's an invalid use of the framework because 
    // Mockito is still waiting to find out what it should do when myMethod is called.
    // But Mockito can't report it yet, because the call to thenReturn might 
    // be yet to happen.
    when(myMock.method1());

    doSomeTestingStuff();

    // ERROR 1 is reported on the following line, even though it's not the line with
    // the error.
    verify(myMock).method2();

}

@Test
public void test2() {

    doSomeTestingStuff();

    // ERROR 2
    // This compiles and runs, but it's an invalid use of the framework because
    // Mockito doesn't know what method call to verify.  But Mockito can't report 
    // it yet, because the call to the method that's being verified might 
    // be yet to happen.
    verify(myMock);
}

@Test
public void test3() {

    // ERROR 2 is reported on the following line, even though it's not even in 
    // the same test as the error.
    doReturn("Hello").when(myMock).method1();


    // ERROR 3
    // This compiles and runs, but it's an invalid use of the framework because
    // Mockito doesn't know what method call is being stubbed.  But Mockito can't 
    // report it yet, because the call to the method that's being stubbed might 
    // be yet to happen.

    doReturn("World").when(myMock);

    doSomeTestingStuff(); 

    //  ERROR 3 is never reported, because there are no more Mockito calls. 
}

Ahora, cuando escribí esta respuesta por primera vez hace más de cinco años, escribí

Así que recomendaría el uso de MockitoJUnitRunnersiempre que sea posible. Sin embargo, como Tomasz Nurkiewicz ha señalado correctamente, no puede usarlo si necesita otro corredor JUnit, como el Spring.

Mi recomendación ahora ha cambiado. El equipo de Mockito ha agregado una nueva función desde que escribí esta respuesta por primera vez. Es una regla JUnit, que realiza exactamente la misma función que MockitoJUnitRunner. Pero es mejor, porque no excluye el uso de otros corredores.

Incluir

@Rule 
public MockitoRule rule = MockitoJUnit.rule();

en tu clase de prueba. Esto inicializa los simulacros y automatiza la validación del marco; como MockitoJUnitRunnerhace. Pero ahora, puede usar SpringJUnit4ClassRunnero cualquier otro JUnitRunner también. Desde Mockito 2.1.0 en adelante, hay opciones adicionales que controlan exactamente qué tipo de problemas se informan.

Dawood ibn Kareem
fuente
Definitivamente no puedo decir que sean iguales. en un caso de prueba, la configuración de junit runner me falla y no inyecta mis simulacros correctamente a menos que realice la configuración de
initMocks
Estamos usando testng 6.8.8 + mockito 1.10.19 y, obviamente, no podemos usar MockitoJUnitRunner, ¡pero el marco de validación aún funciona! Y funciona exactamente como @David Wallace. ¿Alguien puede explicarlo? ¿Es esto porque todavía tenemos devoluciones de llamada @ Before * y MockitoAnnotations.initMocks (esto)?
yuranos
@ yuranos87 Parece algo que deberías hacer como una nueva pregunta. No olvide incluir su código cuando lo haga; es un poco inútil preguntar "¿por qué este código hace XYZ?" Si no muestra el código.
Dawood ibn Kareem
1
el uso de la solución TestRunner supera con creces a @Rule
Regla
1
@alexandroid Mi mejor sugerencia es que escribas tu propia respuesta, usando @ExtendWith. Realmente no es algo que yo sepa. Lo mejor de Stack Overflow es que en una pregunta como esta, puede terminar con múltiples respuestas correctas.
Dawood ibn Kareem
24

El uso de Runner le permite ahorrar un poco de codificación (sin necesidad de @Beforemétodo). Por otro lado, usar un corredor a veces no es posible, es decir, cuando ya está usando uno, comoSpringJUnit4ClassRunner .

Eso es. Es solo una cuestión de preferencia.

Tomasz Nurkiewicz
fuente
2
Aparte de la línea initMocks (), el método @Before aún sería necesario para cualquier otra configuración, ¿verdad?
OceanBlue
2
@OceanBlue: Por supuesto, si su @Beforemétodo contenía algo, excepto initMocks()que debe conservarlo después de migrar al corredor.
Tomasz Nurkiewicz
La respuesta de David Wallace sobre la validación del marco responde a mi pregunta por completo, así que la acepté, pero +1 por señalar que este corredor no se puede usar con otro, como el Spring. ¡Gracias!
OceanBlue
1
Estoy usando Spring Boot y puedo decir que SpringJUnit4ClassRunnerautomáticamente inicializa simulacros para mí. Sin embargo, no sé nada de Spring simple.
gustavohenke