Mockito verify
Cuando hacemos tests con Mockito uno de los puntos importantes es verificar cómo se ha interactuado con los mocks del test para saber si todo ha ido de forma correcta. Mockito.verify() nos ayuda con todo esto. Vamos a ver sus posibilidades desde las más sencillas a otras más complejas
Contexto del código[editar]
Tienes el código completo de los siguientes ejemplos en SomeVerifyTest.java. En ese mismo proyecto tienes las otras clases de apoyo. Están detalladas en Ejemplo sencillo con Mockito. Como resumen y para estos ejemplos, hay tres clases cada una con su método de las que hacemos mock:
dataBaseClass.getStringFromDataBase()simula ser una consulta a una base de datos para obtener un String.networkClass.getStringFromRemoteServer()simula ser una conexión de red por la que nos llegará un String.outputClass.printOutput(String)simula ser una clase a la que enviar un String que queremos que se escriba en algún sitio.
Mockito verify : llamada a métodos[editar]
Lo más directo es verificar que se ha llamado a un determinado método pasándole un determinado parámetro. Por ejemplo
// Que se han llamado a los métodos.
Mockito.verify(outputClass).printOutput("Hello - World");
Mockito.verify(dataBaseClass).getStringFromDataBase();
Mockito.verify(networkClass).getStringFromRemoteServer();
Llamamos a Mockito.verify() pasando como parámetro el mock de la clase que queremos verificar. Luego concatenamos el método y parámetros, si los hay, que esperamos que se hayan llamado. Mockito.verify() hará que falle el test si no ha ocurrido esa llamada.
En el primero se verifica que durante el test se ha llamado a outputClass.printOut() pasando como parámetro el String "Hello - World". En los dos siguientes se verifican que durante el test se ha llamado a los métodos correspondientes de los dos mock restantes. Esos métodos no llevan parámetros.
Mockito Verify : times[editar]
Mockito.verify() admite como primer parámetro el mock a verificar. Pero admite un segundo parámetro con el que podemos poner algunas condiciones para que la llamada se considere válida. Una de ellas es Mockito.times(), con el que podemos indicar cuántas veces esperamos que se haya llamado el método durante el test. Si no ponemos este segundo parámetro, cualquier número de llamadas es válido.
Mockito.verify(outputClass, Mockito.times(1)).printOutput(Mockito.anyString());
Similar al ejemplo anterior, pero aquí indicamos que esperamos que es haya llamado una sola vez. Poniendo como parámetro Mockito.anyString() indicamos que la llamada nos vale si se hace con cualqueir cadena de texto. Podemos poner una cadena concreta como el caso anterior si así lo deseamos. O cualquier otra método de Mockito que nos permita limitar el parámetro.
Hay variantes de Mockito.times() para dar un poco más de flexibilidad
Mockito.verify(dataBaseClass, Mockito.atLeast(1)).getStringFromDataBase();
Mockito.verify(networkClass, Mockito.atMostOnce()).getStringFromRemoteServer();
Mockito.verify(outputClass, Mockito.never()).printPrettyOutput(Mockito.anyString());
Solo hemos puesto tres, pero tienes varias opciones
Mockito.atLeast(n)para verificar que se ha llamado n veces o másMockito.atLeastOnce()para verificar que se ha llamado al menos una vez.Mockito.atMost(n)para verificar que se ha llamado n veces o menos.Mockito.atMostOnce(n)para verificar que se ha llamado como mucho una vez.Mockito.never()para verificar que nunca se ha llamado al método.
Mockito Verify : Orden de llamada a métodos[editar]
Podemos verificar que se ha llamado a los métodos en un orden concreto, como en el siguiente ejemplo
InOrder inOrder = Mockito.inOrder(dataBaseClass, networkClass, outputClass);
inOrder.verify(dataBaseClass).getStringFromDataBase();
inOrder.verify(networkClass).getStringFromRemoteServer();
inOrder.verify(outputClass).printOutput(Mockito.anyString());
Con Mockito.inOrder() pasamos todos los mock que queremos verificar, no es necesario que estén en ningún orden concreto. Obtenemos así una instancia de InOrder.
Ahora sí, tenemos que verificar, en el orden que esperamos que se hayan producido las llamadas, a inOrder.verify(). Si las llamadas se han producido en otro orden, fallará el test.
Mockito verify : Comprobar que se han verificado todas las interacciones con los mock[editar]
En el test, una vez que se han verificado todas las llamadas esperadas a los métodos, podemos verificar también al final que no ha habido ninguna llamada que no hemos verificado.
Mockito.verify(outputClass, Mockito.times(1)).printOutput(Mockito.anyString());
Mockito.verify(dataBaseClass, Mockito.atLeast(1)).getStringFromDataBase();
// Mockito.verify(networkClass, Mockito.atMostOnce()).getStringFromRemoteServer();
Mockito.verifyNoMoreInteractions(outputClass, dataBaseClass, networkClass);
A Mockito.verifyNoMoreInteractions() le pasamos todos los mocks que queremos comprobar que hemos verificado.
Hemos cogido el ejemplo de Mockito verify times y hemos comentado una de las verificaciones. La llamada a Mockito.verifyNoMoreInteractions(outputClass, dataBaseClass, networkClass) dará fallo porque ha habido un mock al que se ha llamado a un método y no lo hemos verificado.
Mockito Verify : Valor de parámetros[editar]
Hemos visto en los ejemplos que podemos verificar que se ha llamado al mock con un parámetro concreto o algún tipo de patrón
Mockito.verify(outputClass).printOutput("Hello - World");
Mockito.verify(outputClass).printOutput(Mockito.anyString());
// y otras variantes disponibles en Mockito
Pero a veces eso no es suficiente, necesitamos analizar el parámetro con el que se ha llamado para saber si la llamada es o no correcta. Para ello tenemos ArgumentCaptor
ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);
Mockito.verify(outputClass).printOutput(argument.capture());
Assertions.assertEquals("Hello - World",argument.getValue());
Primero obtenemos una instancia de ArgumentCaptor con ArgumentCaptor.forClass() pasando como parámetro el tipo de clase del parámetro que queremos capturar. En nuestro ejemplo es un simple String, pero podría ser cualquier clase de nuestro código.
Luego llamamos a Mockito.verify() de la forma habitual, pero pasando como parámetro esperado de la llamada el argument.capture(). Esta llamada a Mockito.verify() no dará error por el parámetro, aunque sí dará error si no se ha hecho ninguna llamada o, en caso de poner el segundo parámetro de Mockito.verify(), si no se cumple la condición que indique dicho parámetro.
Finalmente, con argument.getValue() obtenemos el valor del parámetro para la última llamada que se haya hecho a ese método del mock. Si solo se ha hecho una llamada, este método nos vale. Si se han hecho varias llamadas, podemos llamar a argument.getAllValues(), que nos devolverá los valores del parámetro en todas las llamadas consecutivas.
Una vez tenemos los valores, con las assertions normales de JUnit podemos verificar que el valor del parámetro cumple los criterios que queramos.
Mockito Verify : Se lanza Excepción[editar]
Otra cosa que podemos querer verificar es que la llamada a determinado método ha lanzado una excepción. Esto no se comprueba con Mockito. Se verifica directamente con JUnit.
Para el caso de Junit 4, podemos ponerlo así
@Test(expected = NoSuchElementException.class)
public void exceptionVerify() throws SQLException {
Mockito.when(dataBaseClass.getStringFromDataBase()).thenThrow(new NoSuchElementException());
dataBaseClass.getStringFromDataBase();
}
Simplemente, en la anotación @Test, añadimos (expected = NoSuchElementException.class) con la excepción que esperamos que salte. En el código de test y sólo como ejemplo, hemos configurado el mock dataBaseClass para que lance una excepción NoSuchElementException cuando se haga la llamada a getStringFromDataBase().
Luego hacemos la llamada, que lanzará la excepción porque así lo hemos configurado, y el test pasará con éxito.
Para el caso de JUnit 5, podemos ponerlo de esta otra forma
@Test
public void exceptionVerify() throws SQLException {
Mockito.when(dataBaseClass.getStringFromDataBase()).thenThrow(new NoSuchElementException());
Assertions.assertThrows(NoSuchElementException.class,()->dataBaseClass.getStringFromDataBase());
}
JUnit 5 nos ofrece Assertions.assertThrows() que admite dos parámetros:
- El tipo de excepción que esperamos
NoSuchElementException - Una expresión Lambda con el código que esperamos que lance la excepción, en este ejemplo, la llamada a
dataBaseClass.getStringFromDataBase().
Mockito Verify : Métodos estáticos[editar]
Podemos verificar las llamadas a método estáticos de las clases de nuestro código.
@Test
public void staticVerify(){
try (MockedStatic<StaticClass> staticClassMockedStatic = Mockito.mockStatic(StaticClass.class)){
// Ejecución del test
StaticClass.getName();
// Verificaciones
staticClassMockedStatic.verify(() -> StaticClass.getName(), Mockito.times(1));
staticClassMockedStatic.verify(() -> StaticClass.addOne(Mockito.anyInt()), Mockito.never());
}
}
La primera parte la comentamos con detalle en el tutorial de Mockito When, lo resumimos aquí de todas formas. Dentro de una estructura try-with-resources obtenemos una instancia de MockStatic con la llamada a Mockito.mockStatic(StaticClass.class) siendo StaticClass.class la clase de nuestro código que tiene el método estático que queremos verificar.
Esto hará que dentro del try se capturen todas las llamadas a método estáticos de esa clase y así luego podamos verificarlas. En nuestro ejemplo, la clase tiene dos métodos estáticos getName() y addOne(int). Sólo llamamos a uno de ellos.
Y la forma de verificarlo es llamar al método verify() de la instancia de MockedStatic. Este método admite dos parámetros:
- Una expresión lambada con la llamada al mock que queremos verificar.
- La condición que queremos verificar, en nuestro ejemplo, que
getName()se ha llamado una vez y queaddOne()no se ha llamado nunca con ningún valor entero.