Por qué las pruebas en Go no dan miedo, sino que son necesarias
\n\nCuando escribes en Go por primera vez, parece que el compilador atrapa casi todo: los tipos son estrictos, los errores son explícitos, nil está bajo control. Pero cuando tu proyecto crece a más de 500 líneas, te das cuenta: no se puede vivir sin pruebas. Un cambio en un lugar rompe otro, y te enteras de ello solo en producción.
En este artículo te mostraré por dónde empezar con las pruebas en Go para un desarrollador principiante. Abordaremos las pruebas unitarias, las pruebas tabulares, los mocks y la cobertura de código. Sin rodeos: solo ejemplos prácticos y consejos útiles.
\n\nSi llevas al menos un par de meses escribiendo en Go, este material te ahorrará horas de depuración.
\n\nQué necesitas saber antes de empezar
\n\nGo proporciona el paquete integrado testing — no necesitas instalar nada adicional. Las pruebas se escriben en archivos con el sufijo _test.go y se ejecutan con el comando:
go test ./...\n\nReglas básicas:
\n- \n
- La función de prueba debe llamarse
TestXxx(t *testing.T), donde Xxx es el nombre de la función a probar con mayúscula inicial. \n - Los archivos de prueba se colocan junto al código que se va a probar. \n
- Los errores se notifican mediante
t.Error()ot.Fatal(). \n
Pasemos directamente a la práctica.
\n\nEscribimos nuestra primera prueba unitaria en Go
\n\nSupongamos que tenemos una función simple que suma dos números:
\n\n// calc.go\npackage calc\n\nfunc Sum(a, b int) int {\n return a + b\n}\n\nCreamos el archivo calc_test.go en la misma carpeta:
// calc_test.go\npackage calc\n\nimport "testing"\n\nfunc TestSum(t *testing.T) {\n result := Sum(2, 3)\n expected := 5\n\n if result != expected {\n t.Errorf("Sum(2,3) = %d; want %d", result, expected)\n }\n}\n\nEjecutamos la prueba:
\n\ngo test -v\n\nVerás un PASS en verde: tu primera victoria en pruebas. Observa que usamos t.Errorf (no fatal): la prueba continuará ejecutándose incluso si un caso falla.
Pruebas tabulares: cuando una sola prueba es incómoda
\n\nImagina que necesitas probar una función con una docena de entradas diferentes. ¿Escribir 10 pruebas separadas? No, en Go para eso existen las pruebas tabulares (table-driven tests). Es el estándar en la comunidad Go.
\n\nAñadamos una función de división a nuestro calc.go:
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\nAhora escribimos la prueba tabular:
\n\nfunc TestDivide(t *testing.T) {\n tests := []struct {\n name string\n a, b int\n expected int\n wantErr bool\n }{\n {"positive", 10, 2, 5, false},\n {"negative", -6, 3, -2, false},\n {"zero division", 5, 0, 0, true},\n {"same numbers", 7, 7, 1, false},\n }\n\n for _, tt := range tests {\n t.Run(tt.name, func(t *testing.T) {\n got, err := Divide(tt.a, tt.b)\n if tt.wantErr {\n if err == nil {\n t.Error("expected error, got nil")\n }\n return\n }\n if err != nil {\n t.Errorf("unexpected error: %v", err)\n }\n if got != tt.expected {\n t.Errorf("Divide(%d,%d) = %d; want %d", tt.a, tt.b, got, tt.expected)\n }\n })\n }\n}\n\n¿Qué tiene de genial esto?:
\n- \n
- Cada caso es una subprueba con nombre. Si una falla, las demás se ejecutan. \n
- Es fácil añadir nuevos casos: solo agregas una línea a la estructura. \n
- El código se lee como una especificación. \n
Ejecuta con la bandera -v y verás los nombres de todas las subpruebas. Esto es muy útil para la depuración.