Testes Orientados por Tabela e Subtestes
Casos parametrizados, t.Run e padrões de configuração compartilhada.
Busque em todas as páginas da documentação
Casos parametrizados, t.Run e padrões de configuração compartilhada.
Testes orientados por tabela armazenam casos em uma slice de structs e iteram com um único corpo de teste.
Subtestes envolvem cada linha com t.Run para que as falhas nomeiem o caso e você possa executar subconjuntos com -run.
Configurações compartilhadas pertencem a auxiliares marcados com t.Helper() ou em TestMain quando todo o pacote precisa de um banco de dados.
Cartão de receita de referência rápida - pronto para copiar e colar.
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("esperava erro")
}
return
}
if err != nil || got != tc.want {
t.Fatalf("got (%d, %v) want (%d, nil)", got, err, tc.want)
}
})
}
}Quando usar isso:
-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("esperava erro")
}
return
}
if err != nil {
t.Fatalf("erro 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])
}
}
})
}
}O que isso demonstra:
t.Run rotula falhas (TestParseInts/bad token)t.Parallel() executa subtestes concorrentemente quando segurot.Run registra um subteste com seu próprio escopo de falha e paralelismo opcional.go test -run 'TestParseInts/list$' corresponde a nomes de subtestes por regex.tc para closures para clareza em bibliotecas.| Padrão | Use quando | Notas |
|---|---|---|
Auxiliar com t.Helper() | Asserções repetidas | Mantém os números de linha no teste |
defer em subteste | Limpeza por caso | Executa mesmo quando t.Fatal é acionado |
TestMain | Um DB ou listener global | Chame m.Run() uma vez, depois limpe |
t.Cleanup | Registra a limpeza após a configuração | Executa em ordem LIFO por teste |
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 duplicados confundem os filtros -run. Correção: Use strings name únicas e descritivas.t.Parallel().wantErr ou compare errors.Is na tabela.errors.Is / errors.As na struct.t.Run para que todos os casos sejam executados.| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
TestXxx separado por caso | Regressão única com configuração pesada | Muitos inputs similares |
| Testes generativos / fuzz | Espaço de entrada malformada desconhecido | Regras de negócio documentadas |
| Structs de suíte testify | Hooks de suíte compartilhados entre métodos | Funções puras simples |
| Camadas BDD (Ginkgo, etc.) | Equipe padroniza em prosa BDD | Você quer zero dependências de teste |
Mantenha-a dentro da função de teste, a menos que vários testes compartilhem os mesmos casos.
Tabelas grandes e compartilhadas podem viver em testdata como JSON.
Use t.Skip("razão") dentro desse subteste, ou comente a linha.
Não deixe casos pulados no principal sem um problema vinculado.
Sim - t.Run pode chamar t.Run novamente.
Aninhamento profundo prejudica a legibilidade; prefira nomes planos como network/timeout.
Quando testes em funções diferentes não compartilham estado mutável do pacote.
Tabelas com configuração de DB compartilhada geralmente paralelizam dentro de subtestes após a configuração.
defer func() {
if recover() == nil {
t.Fatal("esperava panic")
}
}()Ou extraia um auxiliar que espera panic por linha.
Use *T ou um campo valid bool quando valores zero são ambíguos (por exemplo, want 0 vs ausente).
Armazene bytes esperados em testdata/ e carregue por linha.
Veja coverage-race-detector-and-golden-files para fluxos de atualização.
Sim - campos de struct podem ser parâmetros de tipo ou usar genéricos em auxiliares.
O padrão de loop permanece o mesmo.
t.Log imprime sempre com -v, ou em caso de falha automaticamente.
Use t.Logf dentro do subteste para contexto específico do caso.
A inicialização por subteste é pequena.
Milhares de subtestes de rede ainda prejudicam o CI - agrupe ou simule I/O em vez disso.
Extraia a slice para uma variável ou função geradora no arquivo de teste.
Mantenha os loops de benchmark separados para evitar medir a configuração.
Arquive arquivos //go:build integration para linhas lentas em um _test.go separado.
O go test padrão permanece rápido.
Versões da Stack: Esta página foi escrita para Go 1.26.x (GC padrão Green Tea, go fix modernizers - verifique o patch na compilação), chi (latest - verifique na compilação), gin (latest - verifique na compilação), echo (latest - verifique na compilação), google.golang.org/grpc (latest - verifique na compilação), sigs.k8s.io/controller-runtime (latest - verifique na compilação), kubebuilder (latest - verifique na compilação), tinygo (latest - verifique os alvos de placa na compilação), wazero (latest - verifique na compilação) e golangci-lint (latest - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026