Contenido del curso
Generar pruebas como un problema de evaluación
La AI como revisor de código
Analisis estático usando la IA como capa de evalulación
Integrar y Calibrar en CI/CD
Mutation testing: cómo detectar puntos ciegos en tus tests
Resumen
El mutation testing es la técnica que separa una suite de tests que realmente protege tu código de una que solo se ve verde. Si escribes tests con ayuda de IA y quieres saber si sirven de verdad, esto te interesa: aprenderás a inyectar bugs conocidos y medir si tus tests los detectan.
La trampa más común es creer que si los tests pasan, el código está bien. Lo verde no es prueba de calidad, es solo ausencia de rojo. La IA produce con facilidad tests decorativos: código que compila, una suite que arranca y termina sin fallar, pero que por dentro no presiona nada.
¿Por qué no debo pedirle a la IA que califique sus propios tests?
La tentación es pedirle a la misma IA que se autoevalúe con un "califica estos tests del uno al 10". No lo hagas. Como editor de tu propio código, tu trabajo es rechazar los puntajes difusos y buscar siempre señales binarias.
¿Qué es una señal binaria en testing? Es un resultado sin ambigüedad: el test detectó el cambio dañino o no lo detectó. A diferencia de un puntaje del 1 al 10, no depende de interpretación.
Un puntaje del uno al 10 no te dice nada accionable. En cambio, saber que un test falló cuando debía fallar sí te da certeza [00:52].
¿Qué es el mutation testing y cómo funciona?
El mutation testing es una técnica para vacunar tu suite con bugs conocidos y ver cómo reacciona. Si tus tests realmente sirven, deberían detectar cambios dañinos en el código [01:11].
Una herramienta automatizada introduce deliberadamente pequeños cambios en tu código fuente. A esos bugs los llamamos mutantes. La herramienta entra en el código, no en el test, y hace cosas como:
- Cambiar un más por un menos.
- Cambiar un mayor que por un mayor o igual a.
- Reemplazar un valor de retorno cero por un none.
Por cada mutante generado, la herramienta corre la suite completa. Y aquí aparece la señal que buscábamos [01:47].
¿Qué significa que un mutante muera o sobreviva?
Este es el corazón del método:
- Mutante matado: un test falló ante el cambio. Tus tests sirven, porque detectaron el daño.
- Mutante sobreviviente: todos los tests siguieron pasando pese al cambio. Tienes un punto ciego que esa lógica no estaba verificando.
Piénsalo como vacunar la suite con enfermedades conocidas para comprobar si el sistema inmune de tu código reacciona [02:20].
¿Cómo corro Cosmic Ray paso a paso?
La herramienta que usamos aquí se llama Cosmic Ray, una librería para Python elegida por su compatibilidad con Windows. Trabajamos sobre refunds.py, con una suite base ya enriquecida para este ejercicio [02:44].
El flujo se resume en cuatro comandos:
- Instalar la dependencia en el entorno de desarrollo, para que sea parte del proyecto y no algo instalado a mano.
- Preparar la corrida, que crea un
mutation.sqlite: una base de datos con todas las partes donde el código puede mutar. - Ejecutar cada mutante contra la suite con
cosmic-ray exec cosmic-ray.toml mutation.sqlite. - Generar el reporte con
cr report mutation.sqlite --t mutation-report.txt.
Al instalar te pedirá indicar sobre qué archivo trabajar; en este caso, refunds.py [03:38].
¿Qué me dice el reporte de mutación?
Lo importante del reporte es concreto: se ejecutaron 46 experimentos sobre refunds.py y de esos, ocho mutantes sobrevivieron [05:00]. En principio, tienes ocho puntos ciegos que analizar.
Un ejemplo real: un valor remaining menor o igual a cero fue mutado a remaining igual a cero y sobrevivió. Eso puede indicar que falta un test para valores negativos o para estados sobrerreembolsados [05:26].
Pero cuidado: no todos los sobrevivientes hay que corregirlos. En Python muchas funciones retornan cualquier tipo de valor, y si tu código no exige que un booleano sea inmutable, cambiar los tests no aporta nada [05:44].
¿Cómo uso la IA para clasificar mutantes sin perseguir el score?
Aquí la IA sí ayuda, pero con un rol claro: clasificar, no calificar. El prompt le pide leer el reporte y no escribir código ni tocar tests. Por cada sobreviviente entrega una tabla con el mutante, su función, qué cambio hizo, qué comportamiento queda sin cubrir y una clasificación [06:14].
Las categorías son:
- Accionable: representa un hueco real de cobertura.
- Equivalente: el cambio no altera el comportamiento observable.
- No accionable: no vale la pena un test para eso.
- Pendiente de decisión: depende de si quieres testear ese contrato.
El análisis mostró que de los ocho, seis apuntaban a huecos reales, uno no se podía matar con un test de comportamiento porque is e == eran indistinguibles para ese caso, y uno quedó pendiente según si querías testear la inmutabilidad de una data class como contrato explícito [06:58].
¿Cómo regenero mis tests para matar mutantes?
Con el reporte clasificado, viene el prompt matamutantes. Su regla central: mejorar la suite sin perseguir mutantes por score. Si no hay contrato claro, no se escribe un test para ese mutante [07:52].
El prompt pide no tocar código de producción, no agregar dependencias y modificar solo el archivo de test. Solo agrega tests para mutantes accionables ligados a comportamientos observables y relevantes para pagos. Nada de tests para type hints, decoradores o mutantes equivalentes [08:15].
El resultado final habla por sí solo: al volver a correr los mismos comandos sobre los mismos 46 mutantes, ahora solo sobrevivieron tres, todos justificados por decisiones conscientes de no tocarlos [09:11]. Pasamos de ocho a tres.
¿Cuándo conviene usar mutation testing?
El mutation testing es una herramienta de evaluación puntual, no de rutina. Es costoso computacionalmente, así que no lo correrás en cada commit [09:44].
Pero su valor real ya está hecho: instalar en ti una disciplina mental. Nunca confíes ciegamente en un test que pasa. Busca tu oráculo. Recuerda que los prompts de recursos no debes copiarlos literal: analízalos y adáptalos a tus proyectos.
¿Ya probaste medir tus tests con mutantes? Cuéntame cuántos sobrevivieron en tu primera corrida.