Pruebas Basadas en Tablas y Subpruebas
Casos parametrizados, t.Run y patrones de configuración compartida.
Busca en todas las páginas de la documentación
Casos parametrizados, t.Run y patrones de configuración compartida.
Las pruebas basadas en tablas almacenan casos en una slice de structs y se ejecutan en un bucle con un único cuerpo de prueba.
Las subpruebas envuelven cada fila con t.Run para que los fallos nombren el caso y puedas ejecutar subconjuntos con -run.
La configuración compartida pertenece a ayudantes marcados con t.Helper() o a TestMain cuando todo el paquete necesita una base de datos.
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
func TestParse(t *testing.T) {
tests := []struct {
name string
input string
want int
wantErr bool
}{
{"zero", "0", 0, false},
{"bad", "x", 0, true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got, err := Parse(tc.input)
if tc.wantErr {
if err == nil {
t.Fatal("se esperaba un error")
}
return
}
if err != nil || got != tc.want {
t.Fatalf("se obtuvo (%d, %v) se esperaba (%d, nil)", got, err, tc.want)
}
})
}
}Cuándo usar esto:
-run TestParse/badt.Parallel()package parse
import (
"strconv"
"strings"
"testing"
)
func ParseInts(csv string) ([]int, error) {
if csv == "" {
return nil, nil
}
parts := strings.Split(csv, ",")
out := make([]int, 0, len(parts))
for _, p := range parts {
n, err := strconv.Atoi(strings.TrimSpace(p))
if err != nil {
return nil, err
}
out = append(out, n)
}
return out, nil
}
func TestParseInts(t *testing.T) {
tests := []struct {
name string
in string
want []int
wantErr bool
}{
{"empty", "", nil, false},
{"single", "42", []int{42}, false},
{"list", "1, 2, 3", []int{1, 2, 3}, false},
{"bad token", "1,x", nil, true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
got, err := ParseInts(tc.in)
if tc.wantErr {
if err == nil {
t.Fatal("se esperaba un error")
}
return
}
if err != nil {
t.Fatalf("error inesperado: %v", err)
}
if len(got) != len(tc.want) {
t.Fatalf("len %d want %d", len(got), len(tc.want))
}
for i := range got {
if got[i] != tc.want[i] {
t.Fatalf("got[%d]=%d want %d", i, got[i], tc.want[i])
}
}
})
}
}Lo que esto demuestra:
t.Run etiqueta los fallos (TestParseInts/bad token)t.Parallel() ejecuta subpruebas concurrentemente cuando es segurot.Run registra una subprueba con su propio ámbito de fallo y paralelismo opcional.go test -run 'TestParseInts/list$' coincide con los nombres de las subpruebas por regex.tc a las clausuras para mayor claridad en las bibliotecas.| Patrón | Usar cuando | Notas |
|---|---|---|
Ayudante con t.Helper() | Afirmaciones repetidas | Mantiene los números de línea en la prueba |
defer en subprueba | Limpieza por caso | Se ejecuta incluso cuando t.Fatal se dispara |
TestMain | Una base de datos o un listener global | Llama a m.Run() una vez, luego limpieza |
t.Cleanup | Registra la limpieza después de la configuración | Se ejecuta en orden LIFO por prueba |
func setupDB(t *testing.T) *sql.DB {
t.Helper()
db, err := sql.Open("sqlite", ":memory:")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { db.Close() })
return db
}name confunden los filtros -run. Solución: Usa cadenas name únicas y descriptivas.t.Parallel().wantErr o compara errors.Is en la tabla.errors.Is / errors.As en la struct.t.Run para que todos los casos se ejecuten.| Alternativa | Usar cuando | No usar cuando |
|---|---|---|
TestXxx separada por caso | Regresión única con configuración pesada | Muchas entradas similares |
| Pruebas generativas / fuzz | Espacio desconocido de entrada mal formada | Reglas de negocio documentadas |
| Structs de suite testify | Hooks de suite compartidos entre métodos | Funciones puras simples |
| Capas BDD (Ginkgo, etc.) | El equipo se estandariza en prosa BDD | Quieres cero dependencias de prueba |
Mantenla dentro de la función de prueba a menos que múltiples pruebas compartan los mismos casos.
Las tablas compartidas grandes pueden vivir en testdata como JSON.
Usa t.Skip("razón") dentro de esa subprueba, o comenta la fila.
No dejes casos omitidos en producción sin un issue vinculado.
Sí - t.Run puede llamar a t.Run de nuevo.
El anidamiento profundo perjudica la legibilidad; prefiere nombres planos como network/timeout.
Cuando las pruebas en diferentes funciones no comparten estado mutable del paquete.
Las tablas con configuración de base de datos compartida generalmente se paralelizan dentro de las subpruebas después de la configuración.
defer func() {
if recover() == nil {
t.Fatal("se esperaba un panic")
}
}()O extrae un ayudante que espere un panic por fila.
Usa *T o un campo valid bool cuando los valores cero sean ambiguos (por ejemplo, want 0 vs. faltante).
Almacena los bytes esperados bajo testdata/ y cárgalos por fila.
Consulta coverage-race-detector-and-golden-files para flujos de trabajo de actualización.
Sí - los campos de struct pueden ser parámetros de tipo o usar genéricos en los ayudantes.
El patrón de bucle no cambia.
t.Log imprime siempre con -v, o en caso de fallo automáticamente.
Usa t.Logf dentro de la subprueba para contexto específico del caso.
El inicio por subprueba es pequeño.
Miles de subpruebas de red aún perjudican a CI - usa lotes o simula I/O en su lugar.
Extrae el slice a una variable o función generadora en el archivo de prueba.
Mantén los bucles de benchmark separados para evitar medir la configuración.
Etiqueta los archivos //go:build integration para filas lentas en un _test.go separado.
El go test predeterminado sigue siendo rápido.
Versiones de Stack: Esta página fue escrita para Go 1.26.x (predeterminado GC Green Tea, go fix modernizers - verifica el parche en la compilación), chi (última - verifica en la compilación), gin (última - verifica en la compilación), echo (última - verifica en la compilación), google.golang.org/grpc (última - verifica en la compilación), sigs.k8s.io/controller-runtime (última - verifica en la compilación), kubebuilder (última - verifica en la compilación), tinygo (última - verifica los objetivos de placa en la compilación), wazero (última - verifica en la compilación), y golangci-lint (última - verifica el conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 16 jul 2026