Pruebas de mutación en Go: cómo detectar lo que los tests unitarios pasan por alto

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

Introducción: ¿Los tests pasan, pero los bugs persisten?

\n\n

Imagine la situación: ha escrito un módulo en Go, lo ha cubierto con tests unitarios al 95% según go test -cover, todos los tests están en verde. Está seguro de que el código funciona perfectamente. Pero en producción aparece un bug. ¿Le suena?

\n\n

El problema es que la métrica de cobertura de código (code coverage) es solo la cantidad de líneas que se ejecutaron. No indica qué es lo que realmente verifican los tests. Se puede tener un 100% de cobertura, pero nunca comprobar que una función devuelve el valor correcto, y no solo que no falla con un pánico.

\n\n

Aquí es donde entran en escena las pruebas de mutación (mutation testing) en Go: una técnica que introduce errores artificialmente en su código y comprueba si los tests pueden detectarlos. Si los tests no fallan con el código mutado, significa que sus tests son inútiles.

\n\n

En este artículo, analizaremos qué son las pruebas de mutación en Go, cómo configurarlas y por qué son imprescindibles para proyectos donde la fiabilidad es crítica. ¡Vamos allá!

\n\n

¿Qué son las pruebas de mutación?

\n\n

Las pruebas de mutación son un método para evaluar la calidad de los tests, en el que se introducen pequeños cambios (mutaciones) en el programa que simulan errores típicos de un desarrollador. Luego se ejecutan los tests. Si al menos un test falla, la mutación está "muerta" y el test funciona. Si todos los tests pasan, la mutación "sobrevive", y eso es una bandera roja: tiene un agujero en sus tests.

\n\n

En Go, las pruebas de mutación a menudo se denominan mutation testing golang, y son especialmente útiles para verificar la lógica de negocio, validadores, analizadores sintácticos (parsers) y otros componentes críticos.

\n\n

Ejemplo de mutación

\n\n

Supongamos que tiene una función simple:

\n\n
func IsEven(n int) bool {\n    return n%2 == 0\n}
\n\n

Una mutación típica es reemplazar == por !=:

\n\n
func IsEven(n int) bool {\n    return n%2 != 0\n}
\n\n

Si su test solo verifica que IsEven(2) no falla, pero no comprueba que el resultado sea true, entonces la mutación sobrevivirá. El test pasará, aunque la función ahora funcione exactamente al revés.

\n\n

¿Por qué la simple cobertura de código no es suficiente?

\n\n

Muchos desarrolladores en Go se acostumbran a confiar en go test -cover. Es una métrica útil, pero engañosa. Consideremos un ejemplo:

\n\n
func Divide(a, b int) (int, error) {\n    if b == 0 {\n        return 0, errors.New("division by zero")\n    }\n    return a / b, nil\n}
\n\n

Test:

\n\n
func TestDivide(t *testing.T) {\n    result, err := Divide(10, 2)\n    if err != nil {\n        t.Fatal(err)\n    }\n    if result != 5 {\n        t.Errorf("expected 5, got %d", result)\n    }\n}
\n\n

¿Cobertura? 100%: se ejecutaron todas las líneas. Pero, ¿qué pasa si alguien accidentalmente reemplaza a / b por a * b? El test no lo notará, porque 10 * 2 = 20, y esperábamos 5. ¿Fallará el test? No, si no verificamos el valor exacto.

\n\n

Las pruebas de mutación en Go detectarán inmediatamente este problema, creando un mutante con la multiplicación y comprobando si el test falla.

\n\n

Herramientas para pruebas de mutación en Go

\n\n

La herramienta principal es go-mutesting. Es una biblioteca popular que crea mutaciones automáticamente y ejecuta los tests. Instalación:

\n\n
go get github.com/zimmski/go-mutesting/cmd/go-mutesting
\n\n

También existen alternativas, como mutesting de la empresa "Go Test It", pero go-mutesting es el estándar de facto.

\n\n

Cómo ejecutarlo

\n\n

Ejecución básica:

\n\n
go-mutesting ./...
\n\n

Este comando encontrará todos los archivos _test.go en el proyecto y comprobará qué mutaciones sobreviven. El resultado se muestra en la consola con un informe detallado.

\n\n

Ejemplo práctico: pruebas de mutación en Go

\n\n

Creemos un paquete simple calculator con funciones de suma y

Blogs

Book Recommendations