Cuando nos metemos en el mundo del testing, es muy común darse cuenta de que probar una pieza de código aislada no es tan sencillo como parece. La mayoría de las veces, nuestras clases no viven solas, sino que dependen de una maraña de otros servicios, bases de datos o APIs RESTful externas que, si las dejáramos entrar en la prueba, convertirían un simple test unitario en una pesadilla de integración lenta y propensa a fallos.
Para solucionar este jaleo, recurrimos a los llamados dobles de prueba o mocks. Básicamente, se trata de crear objetos ‘de mentira’ que imitan la interfaz de los reales, permitiéndonos controlar exactamente qué responden y verificar que nuestro código los llama como debe. Es la única forma de centrarnos en la lógica de negocio sin que nos importe si la base de datos está caída o si el servicio de terceros tarda tres siglos en responder.
Fundamentos de las pruebas locales y unitarias
Antes de tirar de librerías, hay que entender dónde estamos parados. Una prueba local se ejecuta en la JVM de tu propia máquina, saltándose la necesidad de un emulador o un dispositivo físico, lo que dispara la velocidad de ejecución. El problema viene cuando el código toca el framework de Android, ya que al no estar en un dispositivo real, muchas llamadas lanzan excepciones porque el código interno de Android ha sido eliminado en la biblioteca simulable.
Una prueba de unidad debe centrarse en una sección minúscula de código. Si la unidad no fue diseñada para ser testeable (por ejemplo, si tiene métodos privados inalcanzables), tendremos que probarla a través de sus APIs públicas. Para lograr el aislamiento total, sustituimos las dependencias reales por falsificaciones o stubs, evitando que el test dependa de factores externos.
Configuración de dependencias en Android
Para ponerte manos a la obra en Android Studio, los archivos deben vivir en la ruta module-name/src/test/. Para que todo funcione, necesitas añadir las librerías adecuadas en tu build.gradle. Es fundamental usar testImplementation para que estas herramientas no acaben colándose en la versión final de la aplicación que llega al usuario.
Dependiendo de tu gusto o necesidad, puedes optar por JUnit 4 como base, y añadir Mockito, mockito-kotlin o el potente framework MockK. Si te encuentras con el error de ‘Method not mocked’, puedes activar unitTests.returnDefaultValues = true en el Gradle, aunque hay que andar con pies de plomo porque esto podría camuflar bugs devolviendo nulos o ceros en lugar de fallar.
Dominando Mockito y MockK en Kotlin y Java
Mockito es el estándar de oro en Java y ha sido adaptado magistralmente a Kotlin. Para simular una dependencia, creamos un objeto falso con la función mock() y definimos su comportamiento mediante whenever o doReturn(). Esto permite que, al llamar a un método, el mock devuelva exactamente el valor que hemos programado, facilitando la verificación de resultados.
Si necesitas ir un paso más allá, Mockito permite verificar llamadas. Con la función verify() puedes comprobar si un método se ejecutó la cantidad de veces esperada. Además, existe el argument captor, que sirve para atrapar el valor exacto que se le pasó a una función y así hacer una comprobación mucho más exhaustiva del dato procesado.
Simulación de métodos estáticos y funciones de extensión
A veces nos topamos con métodos estáticos que hacen llamadas HTTP reales. En Mockito, esto se soluciona con mockStatic(), creando un simulacro que debe cerrarse obligatoriamente al finalizar el test para no contaminar otras pruebas. Por otro lado, en Kotlin existen las funciones de extensión, que son más complicadas de mockear ya que no pertenecen a la clase en sí, sino que son estáticas por debajo, requiriendo herramientas específicas de MockK para interceptarlas.
Implementación en ecosistemas Spring Boot
En el entorno de Spring, la gestión de beans puede ser un dolor de cabeza. Spring Boot simplifica esto enormemente gracias a la anotación @MockBean. Esta herramienta crea la simulación y la inyecta automáticamente en el contexto del contenedor de Spring, sustituyendo al bean real sin necesidad de crear clases de configuración manuales.
Si usas una versión más clásica de Spring, puedes definir una clase de configuración interna anotada con @Configuration y devolver un mock(Service.class). Si tienes configuraciones en XML, la anotación @Primary es tu mejor aliada para decirle a Spring que prefiera el mock sobre cualquier otra instancia real definida en los archivos de configuración.
El enfoque de simulación en .NET con Moq
Cambiando de aire hacia C#, encontramos que la lógica es muy similar. La librería Moq permite imitar interfaces de acceso a datos para testear el flujo de la aplicación sin tocar la base de datos. Se utiliza el método .Setup() para definir la entrada y .Returns() para el resultado esperado.
Un detalle clave en .NET es el uso de It.IsAny<T>(), que permite que el mock acepte cualquier valor de un tipo específico, haciendo que el test sea más flexible. Para pasar el mock a la clase de servicio, se utiliza la propiedad .Object, y finalmente se emplea .Verify() para asegurar que la interacción con el repositorio ocurrió tal cual se planeó. Comparte la información para que más usuarios conozcan del tema.