El paquete testing y la convención _test.go
En Go, las pruebas forman parte del proyecto y conviven con el código de producción. Los ficheros de prueba terminan obligatoriamente en _test.go y el compilador los excluye de los binarios finales.

Para crear una prueba, declara una función que comience por Test, reciba *testing.T y no devuelva ningún valor:
// calculadora/suma_test.go
package calculadora
import "testing"
func TestSumar(t *testing.T) {
resultado := Sumar(2, 3)
if resultado != 5 {
t.Errorf("Sumar(2, 3) = %d; esperado 5", resultado)
}
}
Ejecuta con:
go test ./calculadora/
go test -v ./... # salida detallada
go test -run TestSumar # ejecutar solo esta prueba
Métodos de *testing.T
| Método | Comportamiento |
|--------|----------------|
| t.Error(msg) | Marca como fallida, continúa la prueba |
| t.Errorf(fmt, ...) | Igual que Error con formato |
| t.Fatal(msg) | Marca como fallida y detiene la prueba |
| t.Fatalf(fmt, ...) | Igual que Fatal con formato |
| t.Log(msg) | Muestra mensaje solo con -v |
| t.Skip(msg) | Omite la prueba (útil para integración) |
| t.Helper() | Marca la función actual como helper |
Subtests con t.Run
Los subtests permiten nombrar y agrupar casos relacionados:
func TestMultiplicar(t *testing.T) {
t.Run("positivos", func(t *testing.T) {
if Multiplicar(3, 4) != 12 {
t.Fail()
}
})
t.Run("por cero", func(t *testing.T) {
if Multiplicar(5, 0) != 0 {
t.Fail()
}
})
t.Run("negativos", func(t *testing.T) {
if Multiplicar(-2, 3) != -6 {
t.Fail()
}
})
}
Ejecutar solo un subtest: go test -run TestMultiplicar/por_cero
Table-driven tests: el patrón idiomático
El patrón más común en proyectos Go es definir una tabla de casos de prueba y recorrerla con un bucle:
func TestDividir(t *testing.T) {
casos := []struct {
nombre string
a, b float64
esperado float64
debeError bool
}{
{"division normal", 10, 2, 5, false},
{"division por cero", 5, 0, 0, true},
{"negativos", -6, 3, -2, false},
{"resultado decimal", 7, 2, 3.5, false},
}
for _, tc := range casos {
t.Run(tc.nombre, func(t *testing.T) {
resultado, err := Dividir(tc.a, tc.b)
if tc.debeError {
if err == nil {
t.Error("se esperaba error pero la función tuvo éxito")
}
return
}
if err != nil {
t.Fatalf("error inesperado: %v", err)
}
if resultado != tc.esperado {
t.Errorf("Dividir(%.1f, %.1f) = %.1f; esperado %.1f",
tc.a, tc.b, resultado, tc.esperado)
}
})
}
}
Helpers con t.Helper()
Cuando extraes lógica de aserción a funciones auxiliares, usa t.Helper() para que los fallos apunten a la línea del test que llamó al helper, no al interior del helper:
func assertIgual(t *testing.T, obtenido, esperado int) {
t.Helper()
if obtenido != esperado {
t.Errorf("obtenido %d, esperado %d", obtenido, esperado)
}
}
func TestCalculadora(t *testing.T) {
assertIgual(t, Sumar(1, 1), 2) // el fallo apunta aquí
assertIgual(t, Sumar(0, 0), 0)
}
Setup y teardown
Go no tiene decoradores ni hooks de ciclo de vida. El patrón idiomático es usar TestMain para configuración global o funciones de limpieza con t.Cleanup:
func TestConBD(t *testing.T) {
db := abrirBDPruebas(t)
t.Cleanup(func() {
db.Close() // se ejecuta al finalizar el test
})
// usar db...
}
func abrirBDPruebas(t *testing.T) *sql.DB {
t.Helper()
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatalf("no se pudo abrir BD de pruebas: %v", err)
}
return db
}
Cobertura de código
go test -cover ./...
go test -coverprofile=cobertura.out ./...
go tool cover -html=cobertura.out # abre informe en el navegador
Un porcentaje de cobertura alto no garantiza la calidad de las pruebas, pero es un indicador útil de áreas sin probar.
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Go es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Go
Explora más contenido relacionado con Go y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender la estructura de un fichero de pruebas en Go (_test.go).
- Escribir funciones de prueba con el tipo *testing.T.
- Usar t.Error, t.Fatal y t.Log para reportar resultados.
- Organizar pruebas con subtests usando t.Run.
- Aplicar el patrón table-driven tests para múltiples casos de prueba.
- Ejecutar pruebas con go test y sus flags principales (-v, -run, -count).