Pruebas de Mutación en Kotlin: cómo escribir código que no teme a los cambios

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Por qué la cobertura de código es una trampa y las pruebas de mutación son la salvación

\n\n

Imagina la situación: tu proyecto en Kotlin tiene una cobertura de pruebas del 95%. El CI está en verde, el gerente está feliz, el código pasa a producción. Y una semana después, un error en producción. ¿Te suena? Lo más probable es que tus pruebas simplemente se ejecuten, pero no verifiquen la lógica. Aquí es donde entran en escena las pruebas de mutación (mutation testing), una metodología que no permite que las pruebas "hagan trampa".

\n\n

En este artículo, analizaremos qué son las pruebas de mutación en Kotlin, cómo funcionan y cómo implementarlas en un proyecto utilizando la popular herramienta Pitest. Aprenderás por qué una cobertura del 100% no es garantía de calidad y cómo las pruebas de mutación ayudan a escribir pruebas verdaderamente confiables.

\n\n

¿Qué son las pruebas de mutación? Te lo explico con gatos

\n\n

Supongamos que escribiste una función que determina si un gato puede pasar por una puerta:

\n\n
fun canCatFitThroughDoor(catWidth: Int, doorWidth: Int): Boolean {\n    return catWidth <= doorWidth\n}
\n\n

Escribiste una prueba:

\n\n
@Test\nfun `gato de ancho 30 pasa por puerta de 40`() {\n    assertTrue(canCatFitThroughDoor(30, 40))\n}
\n\n

Cobertura: 100%. Pero, ¿qué pasa si alguien cambia accidentalmente <= por <? ¡La prueba aún pasará! Las pruebas de mutación introducen automáticamente estas "pequeñas roturas" (mutaciones) en el código y verifican si al menos una prueba falla. Si la prueba no falla, tus pruebas no detectan ese error. Esto es lo que son las pruebas de mutación en Kotlin.

\n\n

Cómo funcionan las pruebas de mutación: desde operadores hasta mutantes "supervivientes"

\n\n

La herramienta (por ejemplo, Pitest para Kotlin) crea mutantes: versiones de tu código con un solo cambio. Estas son las mutaciones típicas:

\n\n
    \n
  • Cambio de operadores de comparación: > → >=, == → !=
  • \n
  • Inversión de condiciones: if (a > b) → if (a <= b)
  • \n
  • Eliminación de llamadas a métodos: someMethod() → vacío
  • \n
  • Sustitución de valores de retorno: true → false
  • \n
  • Cambio de números: 10 → 11 o 0
  • \n
\n\n

Después de crear los mutantes, se ejecutan todas tus pruebas. Los resultados se dividen en tres categorías:

\n\n
    \n
  • Mutantes eliminados (Killed) — las pruebas fallaron, la mutación fue detectada. ¡Excelente!
  • \n
  • Mutantes supervivientes (Survived) — las pruebas pasaron, aunque el código fue modificado. Una señal de alerta.
  • \n
  • Cubiertos pero no eliminados (Covered but not killed) — las pruebas se ejecutaron, pero no notaron el cambio.
  • \n
\n\n

Tu objetivo es tener la menor cantidad posible de mutantes supervivientes (idealmente 0). Así es como funcionan las pruebas de mutación de código Kotlin en la práctica.

\n\n

Pitest: la herramienta principal para Kotlin

\n\n

Para los lenguajes JVM (Java, Kotlin), el estándar de facto es Pitest (PIT Mutation Testing). Soporta Kotlin a través del plugin pitest-kotlin. Así es como se configura en Gradle:

\n\n
plugins {\n    id("info.solidsoft.pitest") version "1.15.0"\n}\n\npitest {\n    targetClasses.set(listOf("com.myapp.*")) // qué clases mutar\n    targetTests.set(listOf("com.myapp.*"))    // qué pruebas ejecutar\n    threads.set(4)                            // hilos paralelos\n    outputFormats.set(listOf("XML", "HTML"))  // informes\n    mutationThreshold.set(80)                 // porcentaje mínimo de eliminados\n    timeoutConstInMillis.set(10000)           // tiempo de espera por prueba\n}\n\ndependencies {\n    testImplementation("org.pitest:pitest-kotlin:1.15.0")\n}
\n\n

Ejecución con un solo comando:

\n\n
./gradlew pitest
\n\n

Después de la ejecución, en build/reports/pites

Blogs

Book Recommendations