CAP 09 · LEC 01·Stdlib y herramientas

Testing: `go test`, table-driven tests y `t.Run`

Go trae testing en la librería estándar. Sin frameworks, sin configuración: archivos `_test.go`, funciones `TestX` y `go test`. El patrón table-driven es el idioma natural para cubrir muchos casos con poco código.

● INTERMEDIO8 min lecturapor Fernando Herrera · actualizado mayo de 2026
¿Encontraste un error o algo que mejorar?Editá esta lección en GitHub →

Tu primer test

Las pruebas en Go viven junto al código que prueban. Si tienes math.go, los tests van en math_test.go en el mismo paquete. La herramienta go test descubre automáticamente los archivos que terminan en _test.go y ejecuta cualquier función con la firma func TestXxx(t *testing.T).

package math func Add(a, b int) int { return a + b }
package math import "testing" func TestAdd(t *testing.T) { got := Add(2, 3) want := 5 if got != want { t.Errorf("Add(2, 3) = %d; want %d", got, want) } }
Salida=== RUN TestAdd --- PASS: TestAdd (0.00s) PASS ok example/math 0.123s

Reglas clave:

  • El nombre del archivo debe terminar en _test.go.
  • La función debe empezar por Test y la siguiente letra debe ir en mayúscula (TestAdd, no Testadd).
  • Recibe un único parámetro: t *testing.T.

`t.Errorf` vs `t.Fatal`

El parámetro t es tu interfaz con el runner de tests. Los dos métodos más usados para reportar fallos son t.Errorf y t.Fatal.

package math import "testing" func TestDivide(t *testing.T) { result, err := Divide(10, 2) // t.Fatal: aborta el test inmediatamente. // Útil cuando lo que sigue no tiene sentido sin esto. if err != nil { t.Fatalf("Divide returned unexpected error: %v", err) } // t.Errorf: registra el fallo pero sigue ejecutando. // Útil cuando puedes seguir comprobando otras condiciones. if result != 5 { t.Errorf("Divide(10, 2) = %d; want 5", result) } }
Regla práctica

Usa t.Fatal cuando el resto del test depende de la condición (por ejemplo, un error al abrir un archivo). Usa t.Errorf cuando quieres reportar todos los fallos de un mismo test en una sola ejecución.

Table-driven tests

El patrón más idiomático en Go: define una tabla (un slice de structs) con los casos a probar y itera. Reduces duplicación y haces trivial añadir nuevos casos.

package math import "testing" func TestAddTable(t *testing.T) { tests := []struct { name string a, b int want int }{ {"positivos", 2, 3, 5}, {"con cero", 0, 7, 7}, {"negativos", -4, -6, -10}, {"mixto", -5, 10, 5}, } for _, tc := range tests { got := Add(tc.a, tc.b) if got != tc.want { t.Errorf("%s: Add(%d, %d) = %d; want %d", tc.name, tc.a, tc.b, got, tc.want) } } }
Salida--- PASS: TestAddTable (0.00s) PASS

La struct anónima permite definir el shape del caso de prueba en línea. El campo name se usa para identificar qué caso falló sin necesidad de mensajes complicados.

Subtests con `t.Run`

Combinar table-driven con t.Run te da subtests independientes: cada caso aparece como un test propio en la salida, se puede ejecutar de forma aislada con -run, y un t.Fatal en uno no detiene el resto.

package math import "testing" func TestAddSubtests(t *testing.T) { tests := []struct { name string a, b int want int }{ {"positivos", 2, 3, 5}, {"con cero", 0, 7, 7}, {"negativos", -4, -6, -10}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := Add(tc.a, tc.b) if got != tc.want { t.Errorf("Add(%d, %d) = %d; want %d", tc.a, tc.b, got, tc.want) } }) } }
Salida=== RUN TestAddSubtests === RUN TestAddSubtests/positivos === RUN TestAddSubtests/con_cero === RUN TestAddSubtests/negativos --- PASS: TestAddSubtests (0.00s) --- PASS: TestAddSubtests/positivos (0.00s) --- PASS: TestAddSubtests/con_cero (0.00s) --- PASS: TestAddSubtests/negativos (0.00s)
Espacios en nombres

Los espacios en tc.name se convierten automáticamente en guiones bajos en la salida y al usar -run. Por eso con cero aparece como con_cero.

Ejecutar tests

go test es el comando que ejecuta toda la maquinaria. Las flags más útiles:

// Ejecutar tests del paquete actual // go test // Verbose: muestra cada test y su resultado // go test -v // Ejecutar todos los tests del módulo (recursivo) // go test ./... // Filtrar por nombre con regex // go test -run TestAdd // go test -run TestAddSubtests/positivos // Cobertura de código // go test -cover // go test -coverprofile=cover.out // go tool cover -html=cover.out // Repetir y detectar tests inestables // go test -count=10 // Detectar condiciones de carrera // go test -race
Salidaok example/math 0.231s coverage: 92.3% of statements
Buenas prácticas

Mantén tests pequeños y deterministas. Si un test depende de tiempo, red o filesystem, márcalo y considera moverlo a _integration_test.go con la build tag //go:build integration. Para cualquier ayuda al diagnóstico, usa t.Logf — solo se imprime con -v o cuando el test falla.