Uno de los patrones más comunes en los frameworks de testing con mocks es el famoso expect-run-verify (o, como se hace en EasyMock, record-replay-verify). Y con el tiempo se está volviendo algo de lo más molesto... veamos porque:
- Ensuciamos el código. O dicho de otra forma, el código de los tests va quedando horrible, a medida que van aparienciendo todos esos expect. Estamos obligados a programar los expect no porque los necesitamos testear, sino porque sino el mock va a chillar feo.
- No resulta natural al ciclo de testeo. Las llamadas esperadas a un mock son en si aserciones, y deberían comprobarse después de la ejecución. Hacerlo al revés no resulta muy intuitivo.
- Los tests quedan frágiles. Los cambios en el código que requieren una interacción nueva entre objetos seguramente romperán a los tests, ya que no contarán con los expect necesarios.
- Es dificil de identificar errores. Si pudieramos transformar todos esos expect en asersiones, al fallar sabríamos exactamente dónde está el problema. Al tener los expect antes, las fallas ocurren dentro del código, en medio de la ejecución (y debemos luego decifrar excepciones para ubicar el lugar).
Presentando Mockito
Mockito es una librería Java para la creación de Objetos Mock muy usados para el testeo unitario en TDD, basado en EasyMock. Mockito fue creado con el objetivo de simplificar y solucionar algunos de los temas antes mencionados. EasyMock y Mockito pueden hacer exactamente las mismas cosas, pero Mockito tiene un API más natural y práctico de usar.
Las características principales de Mockito son:
- Se pueden crear mocks de interfaces y clases concretas.
- Verificación de invocaciones (cantidad exacta, al menos una vez, órden de invocación, etc.)
- El stack trace se mantiene limpio, ya que los errores ocurren en los assert que se hagan (y no dentro del método bajo prueba, como en EasyMock).
- Un API más clara para crear stubs y verificaciones
Un ejemplo con Mockito
Veamos un ejemplo sencillo de invocación a un método
import static org.mockito.Mockito.*; ..... //creamos el mock ArrayList instance = mock(ArrayList.class); //ejecutamos la lógica a probar instance.add("hola"); //verificamos verify(instance).add("hola");
Ahora crearemos un stub de un método para que devuelva valores
import static org.mockito.Mockito.*; ..... //creamos el mock y el stub ArrayList instance = mock(ArrayList.class); doReturn("hola mundo").when(instance).get(0); //ejecutamos la lógica a probar instance.get(0); //verificamos que se hayan invocado los métodos verify(instance).get(0);
Stubs
La sintaxis para crear stubs es más simple de leer que en EasyMock, evitando varios paréntesis anidados. Sin embargo, es posible usar una sintáxis parecida a EasyMock. Por ejemplo, las dos líneas siguientes son equivalentes para mockear un método del ArrayList:
doReturn("hola mundo").when(instance).get(0); stub(instance.get(0)).toReturn("hola mundo");
EasyMock vs. Mockito
Mockito les resultará muy familiar para quienes conozcan EasyMock. Y es que Mockito puede hacer todo lo que EasyMock, y de hecho es un fork de este proyecto. Simplemente cambia algunos comportamientos para facilitar su uso en el día a día.
Sin embargo, hay algunas diferencias. En Mockito:
- sólo existe un tipo de mock (no hay mocks "nice", "default", "strict"). Los mocks de Mockito pueden ser "nice" o "strict", pero eso depende de cómo se verifican.
- no hay record/replay, sino stub/verify. El stub ocurre antes de la ejecución, el verify después.
- todos los mocks son "nice", es decir, devuelven valores al invocarse (y las colecciones se devuelven vacias en vez de nulas).
- se usa un modelo de stubs más simple. Los métodos con stub funcionan continuamente, sin importar cuántas veces se los invoque.
- la verificación de los métodos stub es opcional.
- la verificación es explícita y luego de la ejecución, facilitando la lectura del stack trace.
La página Mockito vs. EasyMock tiene muchos ejemplos comparando el uso de ambos frameworks.
Proyecto de ejemplo
Pueden descargar un proyecto de ejemplo de Mockito, donde se muestran diferentes invocaciones e integración con JUnit y contiene todas las librerías necesarias para empezar.
Por otro lado, pueden descargar la librería desde la página oficial de Mockito, donde también encontrarán más ejemplos.