Continuamos con la serie de posts sobre pruebas unitarias, gestión de dependencias en dichas pruebas y test doubles. Por si te has perdido alguno de los posts de la serie, aquí tienes una pequeña recopilación:
– ¿Qué estoy probando y cuáles son mis dependencias en testing?
– Simulando las dependencias en las pruebas unitarias. Dummies vs Stubs vs Mocks vs Spies vs Fakes
– Algunos ejemplos en código de los distintos tipos de Test Doubles
—-
Cómo vimos en el post de la semana pasada, los distintos tipos de Test Doubles (objetos que nos permiten simular dependencias en otros objetos), se pueden crear de dos maneras:
– programándolos tú (creando las clases, interfaces necesarias, creando objetos que tienen los valores adecuados como parámetros, etc. En el post de Algunos ejemplos en código de los distintos tipos de Test Doubles puse ejemplos de cómo programar esos Test Doubles sin la ayuda de ninguna herramienta)
– o ayudándote de librerías preparadas para la creación de Test Doubles.
Estas librerías de prueba que facilitan la creación de Test Doubles son los frameworks para hacer Mocks (Mocking Framework).
¿Es que cada vez que quieras simular una dependencia en una prueba unitaria vas a tener que estar creando clases nuevas? ¿Creando objetos con parámetros predefinidos? Los frameworks para hacer Mocks nos evitan hacer todo eso.
Estas librerías nos dan herramientas para simular Test Doubles, sin tener que crearnos nuevas clases que aumenten la complejidad de nuestro código. Para ello, podemos indicar directamente qué esperamos que haga nuestro objeto mockeado: por ejemplo, que si se llama a un método concreto de ese objeto, éste devuelva un valor predefinido, sin tener que crear nuevas clases, ni modificar las que ya tenemos.
Está bien que sepamos que podemos gestionar dependencias a mano, y puede que si las dependencias sean sencillas o que no tardamos nada en crear clases simuladas, con eso nos baste. Pero hay alternativa a tener que gestionar todas las dependencias de esa forma J
Nuevamente, no te líes por el nombre, puesto que dichos frameworks no solo sirven para hacer Mocks. Como ya veremos a lo largo del post, con ellas puedes crear Mocks, Stubs, Spies…lo que necesites.
¡Vayamos al ejemplo!
Voy a plantear una situación sencilla (y que conste que no tiene nada que ver con algo que me haya pasado esta mañana en algún atasco ni nada de eso, o puede que tal vez sí).
Imaginemos que tenemos una clase Conductor, que solo depende del Vehículo que conduce dicho Conductor (Conductor tiene un atributo Vehiculo).
Ante una cierta situación, el conductor puede optar por hacer una maniobra pacífica de frenado con su Vehículo o no. Si algo o alguien le ha ofendido, el conductor insultará, tocará el Claxon del Vehículo y frenará, con lo que la maniobra será un éxito, pero no será pacífica.
Si nadie le ha ofendido, el Conductor solo frenará el Vehículo, con lo que la maniobra será un éxito y además, será pacífica.
Este sería el código que modela lo que acabo de explicar:
Lo que queremos hacer es probar si recibiendo las entradas adecuadas, el conductor ejecuta una maniobra pacífica si no está ofendido, y la acaba liando si está ofendido. Además, en ambos casos (ya sea la maniobra pacífica o no), la maniobra es un éxito, porque hemos frenado, la hemos hecho.
No nos importa cómo sea el vehículo del conductor, nuestro SUT es el Conductor, y la dependencia a simular es el Vehículo.
1er enfoque: Mock programado creando una clase propia
Como nuestro código tiene una interfaz IVehiculo, una primera opción para simular la dependencia es crear nuestra propia implementación de dicha interfaz, y pasarla en la construcción del objeto Conductor.
En este caso, y para introducir los ejemplos que quedaban por poner de Test Doubles en el post anterior, vamos a hacer un Mock, es decir, un objeto que va guardando las acciones que se van haciendo al llamar a sus métodos (lo que sería un puro Spy) y al que además le indicamos el comportamiento que debe seguir cuando se llama a sus métodos.
Y te preguntarás, ¿por qué en este caso un Mock? Pues porque en mi test:
- Primero quiero comprobar que independientemente de si el conductor está ofendido o no, la maniobra tiene que ser un éxito. Y para ello, mi Vehiculo al ejecutar los métodos pisarFreno() y tocarClaxon() deberá devolver true. Ahí estoy configurando el comportamiento que debe seguir el objeto cuando llaman a sus métodos.
- Para comprobar si la maniobra fue pacífica o no, voy a ver el número de veces que el conductor tocó el Claxon. Si es 0, no estaba enfadado, pero si es 1, el conductor se enfadó y por lo tanto la maniobra no fue pacífica. Esto es puro comportamiento de Spy, de alguna manera, tener algo que me vaya registrando lo que hace la dependencia, en este caso, variables que me guardan el número de veces que se accedió a ciertos métodos.
NOTA: Este planteamiento que acabo de contar no tiene por qué ser siempre así, lo he hecho para que tengas un ejemplo de qué sería un Mock y qué sería un Spy, y la decisión de hacer un Mock viene dada por cómo he decidido probar el SUT.
Por lo tanto, este sería un ejemplo de mi clase Mock:
Y estas serían mis dos pruebas unitarias:
2º enfoque: Usando Mockito
En este caso voy a utilizar Mockito para gestionar la dependencia.
Para más información sobre cómo se utiliza, cómo configurarlo con Maven, Gradle, etc., entra en la documentación oficial.
La idea va a ser la misma a la hora de hacer las pruebas unitarias: tener un Mock de Vehiculo que registre cuántas veces se ha llamado a los métodos, y poder indicarle que devuelva true al llamar a los métodos del Vehículo.
En este caso, no tenemos que crear esa clase VehiculoMock, sino indicar lo que queremos que tenga nuestro Mock dentro del propio test:
Con herramientas propias de Mockito, como son when, verify y times estamos consiguiendo lo mismo que creando nuestra propia clase vehiculoMock:
– Con when() gestionamos comportamientos: cuando se llame a este método del Mock devuelve esto.
– Con verify() y times estamos comprobando que se llame ese número de veces a los distintos métodos. ¡No necesitamos que nuestro Mock tenga una variable que vaya registrando los valores!
Terminando…
Como ves, existen muchas formas de hacer las cosas, lo importante es tener claro qué quieres hacer y con qué objetivo.
Por último, si te decides a probar las herramientas para hacer Mocks, ten en cuenta que cada una de ellas hace ciertas cosas que otras no hacen. Infórmate bien antes de usar una herramienta, de si se adapta a tus necesidades o no.
JMockit (otro framework para hacer Mocks) tiene una tabla comparativa entre los frameworks de mockeo más conocidos, por si te sirve de ayuda.
- Debes crear apps sin saber programar (no hay que saber nada) + Crea Test con IA + Scrum es el nuevo Excel - 12 septiembre, 2024
- Las 6 técnicas prompting + 1ª Ley del Manager Oscuro + Mantenlo sencillo, estúpido - 5 septiembre, 2024
- Guía de Métricas Ágiles (versión agosto 2024) - 22 agosto, 2024
En general, los métodos que devuelven boolean, deberían tener en el nombre si la acción que describe es realizable o no. En caso contrario, no deberían devolver boolean.
el link a JMockit no esta funcionando
Excelente ejemplo, facil y concreto, explica el porqué y el como…