Resposta rápida: as flags mais úteis do go test resolvem cinco problemas diferentes: -run filtra testes e subtests, -count=1 força uma execução sem cache, -shuffle=on muda a ordem para revelar dependências ocultas, -race procura acessos concorrentes incompatíveis e -json produz eventos estruturados para CI. No dia a dia, comece com go test ./...; quando algo falhar, reduza o escopo com -run, repita com -count, reproduza a seed do -shuffle e só então amplie o diagnóstico.
O comando go test parece simples, mas suas flags transformam a experiência de investigar uma suíte grande. Saber combiná-las evita três desperdícios comuns: rodar o repositório inteiro para depurar um único caso, acreditar que um resultado em cache foi uma nova execução e tentar entender um teste instável sem preservar a ordem ou a repetição que provocou a falha.
Este guia funciona como referência operacional. Para aprender a escrever testes unitários, table-driven tests, mocks, httptest, fuzzing e cobertura desde o começo, leia primeiro o guia completo de testes em Go. Aqui o foco é executar, filtrar, repetir e diagnosticar os testes que já existem.
Tabela rápida das principais flags do go test
| Objetivo | Comando recomendado |
|---|---|
| Testar todo o módulo | go test ./... |
| Ver testes e logs | go test -v ./... |
| Rodar um teste exato | go test -run '^TestCriarPedido$' ./internal/pedido |
| Rodar um subtest | go test -run 'TestCriarPedido/sem_estoque' ./internal/pedido |
| Ignorar o cache | go test -count=1 ./... |
| Repetir um teste 20 vezes | go test -run '^TestFila$' -count=20 ./internal/fila |
| Randomizar a ordem | go test -shuffle=on ./... |
| Reproduzir uma ordem | go test -shuffle=1740001234 ./... |
| Definir limite de tempo | go test -timeout=2m ./... |
| Detectar data races | go test -race ./... |
| Gerar eventos JSON | go test -json ./... |
| Medir cobertura | go test -cover ./... |
| Gerar perfil de cobertura | go test -coverprofile=coverage.out ./... |
| Rodar apenas benchmarks | go test -run='^$' -bench=. -benchmem ./... |
Para consultar todas as opções disponíveis no toolchain instalado, rode:
go help test
go help testflag
Essa consulta local é importante porque o comportamento acompanha a versão de Go usada pelo projeto. O guia de documentação Go em português explica como combinar go help, go doc e pkg.go.dev sem depender de snippets desatualizados.
-run: filtre testes e subtests com expressão regular
A flag -run recebe uma expressão regular aplicada ao nome do teste. Para executar exatamente TestCriarPedido, use âncoras:
go test -run '^TestCriarPedido$' ./internal/pedido
Sem ^ e $, a expressão pode casar com outros nomes, como TestCriarPedidoComCupom. Isso não é necessariamente ruim, mas pode confundir quando você pensa que isolou um único caso.
Subtests criados com t.Run formam um caminho separado por barras. Considere:
func TestCriarPedido(t *testing.T) {
t.Run("sucesso", func(t *testing.T) {
// ...
})
t.Run("sem estoque", func(t *testing.T) {
// ...
})
}
Você pode executar somente o segundo cenário:
go test -run 'TestCriarPedido/sem_estoque' ./internal/pedido
O Go normaliza espaços nos nomes de subtests para facilitar a seleção. Ainda assim, prefira nomes curtos e descritivos. Em uma suíte baseada em tabelas, o nome do caso vira uma ferramenta de debugging, não apenas documentação. Veja padrões completos no artigo sobre table-driven tests em Go.
Combine -run com -v
Quando você precisa ver t.Log, nomes de subtests e duração:
go test -v -run '^TestCriarPedido$' ./internal/pedido
-v é ideal para investigação humana. Evite deixá-lo como única fonte de diagnóstico no CI: milhares de linhas sem estrutura ficam difíceis de pesquisar e correlacionar. Para automação, a saída JSON é melhor.
-count: ignore o cache ou repita o mesmo teste
Resultados bem-sucedidos de testes podem ser armazenados no cache do comando go. Quando o cache é usado, a saída inclui (cached). Isso acelera muito o ciclo normal, mas pode esconder a diferença entre “o código continua igual” e “o teste realmente executou de novo”.
Para forçar uma execução:
go test -count=1 ./...
O mesmo flag também repete cada teste selecionado:
go test -run '^TestWorker$' -count=50 ./internal/worker
Essa repetição ajuda a encontrar flakiness causada por concorrência, relógio, ordem, portas, arquivos temporários ou estado global. Ela não substitui uma reprodução controlada: se o teste falhar na repetição 37, preserve também logs, seed, versão do Go, sistema operacional e configuração relevante.
Quando -count=1 não conserta o problema
Se um teste passa apenas com cache desativado, investigue a causa. Suspeitos frequentes:
- dependência de um arquivo ou serviço externo;
- uso de
time.Nowsem controle; - variáveis de ambiente alteradas por outro teste;
- banco compartilhado sem limpeza;
- variável global ou singleton mutável;
- teste que depende de ordem;
- goroutine que continua viva depois do caso terminar.
Use t.TempDir, t.Setenv e t.Cleanup para isolar recursos. Para serviços reais como PostgreSQL ou Redis, prefira ambientes descartáveis e veja Testcontainers em Go.
-shuffle: encontre dependências ocultas de ordem
Uma suíte deve produzir o mesmo resultado independentemente da ordem dos testes, desde que os casos sejam realmente isolados. -shuffle=on randomiza a ordem de execução dos testes e benchmarks:
go test -shuffle=on ./...
No início da execução, o comando informa a seed usada. Se ocorrer uma falha, copie esse número para reproduzir a ordem:
go test -shuffle=1740001234 ./...
A seed é parte do relatório de incidente. Sem ela, “falhou uma vez com shuffle” é pouco acionável.
-shuffle costuma revelar:
- um teste que cria um registro e outro que presume que ele já existe;
- configuração global que não é restaurada;
- servidor ou porta fixa reaproveitada;
- cache em memória compartilhado entre casos;
- mocks com expectativas mantidas fora do subtest;
- dependência acidental do nome ou da ordem alfabética.
Uma estratégia útil no CI é manter o job rápido e determinístico em cada pull request e rodar um job periódico com -shuffle=on -count=1. Se ele falhar, publique a seed nos artefatos. Randomização sem possibilidade de reprodução só cria ruído.
-race: detecte acessos concorrentes incompatíveis
O race detector instrumenta o programa e reporta data races observadas durante a execução:
go test -race ./...
Ele é especialmente relevante para caches, maps compartilhados, workers, pools, handlers que alteram estado e qualquer código com goroutines. Porém, há dois limites importantes:
- o detector só encontra caminhos que os testes realmente executaram;
- a instrumentação aumenta o tempo e o consumo de memória.
Por isso, repositórios grandes costumam manter -race em um job separado. Ao investigar uma falha específica, combine filtros:
go test -race -run '^TestCacheConcorrente$' -count=20 ./internal/cache
Não “corrija” uma race apenas colocando um mutex em volta de tudo. Primeiro identifique qual estado é compartilhado, quem deveria ser seu dono e qual contrato de sincronização faz sentido. O guia de sync.Mutex, RWMutex e WaitGroup e a cheatsheet de concorrência ajudam a escolher a primitiva correta. Para mensagens típicas do detector, consulte a página de data race em Go.
-timeout: transforme travamento em falha diagnosticável
O go test possui um timeout padrão, mas suítes de CI devem declarar um limite coerente com o projeto:
go test -timeout=2m ./...
Quando o prazo expira, o processo gera informações das goroutines, úteis para localizar deadlocks, leitura de channel sem produtor, servidor que não encerra ou espera externa sem deadline.
Não use um timeout enorme para esconder lentidão. Separe testes rápidos de integração e ponta a ponta. Um pacote de unit tests que precisa de 20 minutos provavelmente mistura responsabilidades ou chama infraestrutura externa sem controle.
Para um teste individual que trabalha com contexto, prefira também um deadline interno:
func TestProcessar(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
if err := Processar(ctx); err != nil {
t.Fatal(err)
}
}
O timeout global protege a suíte; o contexto documenta e testa o contrato da operação.
-json: saída estruturada para CI e relatórios
Para consumo humano, -v é suficiente. Para máquinas, use:
go test -json ./...
A saída contém eventos JSON por linha, com informações como pacote, teste, ação, timestamp, tempo decorrido e texto produzido. Ferramentas de CI podem transformar esses eventos em relatórios, anexar logs ao teste correto e destacar apenas falhas.
Uma execução pode ser salva para processamento posterior:
go test -json ./... > test-results.jsonl
O formato é uma sequência de objetos JSON, não um único array. Portanto, trate o arquivo como JSON Lines. Não tente fazer json.Unmarshal de todo o conteúdo diretamente em uma slice sem antes ler linha a linha.
Para converter a saída de um binário de teste ou de outro comando compatível, existe também go tool test2json. Na maioria dos pipelines comuns, go test -json já é a opção mais simples.
O que guardar quando o CI falha
Um relatório útil inclui:
- pacote e nome completo do teste/subtest;
- commit e versão do Go (
go version); - sistema operacional e arquitetura;
- duração;
- seed do
-shuffle, quando usada; - número de repetições e flags;
- stack trace ou relatório do race detector;
- logs associados somente ao teste que falhou.
Evite imprimir tokens, strings de conexão ou payloads sensíveis nos logs. Estrutura não elimina risco de segredo vazado; apenas facilita localizar o vazamento.
Cobertura: -cover, -coverprofile e -coverpkg
Para um resumo:
go test -cover ./...
Para gerar um perfil:
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
go tool cover -html=coverage.out
Quando o fluxo atravessa vários pacotes, amplie a instrumentação:
go test -coverpkg=./... -coverprofile=coverage.out ./...
Cobertura mostra quais statements foram executados, não se as verificações estavam corretas. Uma porcentagem alta com assertions fracas pode oferecer menos proteção que uma suíte menor cobrindo regras críticas, erros e limites. Use a tendência para detectar regressões, não como pontuação isolada da equipe.
Combinações prontas para problemas reais
Depurar um teste específico
go test -v -run '^TestCriarPedido$/^sem_estoque$' -count=1 ./internal/pedido
Caçar um teste instável
go test -run '^TestWorker$' -count=100 -shuffle=on ./internal/worker
Se falhar, repita com a seed impressa e reduza o -count apenas depois de conseguir uma reprodução estável.
Investigar concorrência
go test -race -run '^TestCache' -count=20 -timeout=3m ./internal/cache
Gerar relatório para CI
go test -json -count=1 -timeout=5m ./... > test-results.jsonl
Rodar cobertura sem reaproveitar cache
go test -count=1 -coverpkg=./... -coverprofile=coverage.out ./...
Rodar somente benchmarks
go test -run='^$' -bench=. -benchmem -count=5 ./...
Para interpretar ns/op, B/op, allocs/op, b.Loop e perfis, siga o guia de benchmarks em Go.
Pipeline recomendado: rápido no PR, profundo em jobs separados
Um pipeline pragmático não precisa colocar todas as flags no mesmo comando.
Job rápido de pull request:
go vet ./...
go test -count=1 -timeout=3m ./...
Job de concorrência:
go test -race -count=1 -timeout=10m ./...
Job periódico de isolamento:
go test -shuffle=on -count=1 -timeout=10m ./...
Job de cobertura:
go test -coverpkg=./... -coverprofile=coverage.out ./...
Essa separação deixa o feedback principal rápido e mantém diagnósticos caros visíveis. Para completar o pipeline com lint, vulnerabilidades e integração, veja TDD e CI/CD com Go, golangci-lint e Staticcheck e govulncheck.
Erros comuns ao usar flags do go test
- Usar
-run Nomesem âncoras e executar mais casos do que imaginava. Use^Nome$quando quiser correspondência exata. - Colocar
-count=1em todo comando local por hábito. O cache existe para acelerar o ciclo; desative-o quando precisar de uma nova execução. - Rodar
-shuffle=onsem guardar a seed. Uma falha não reproduzível vira investigação por tentativa e erro. - Concluir que
-raceprova ausência de races. Ele só observa caminhos executados. - Aumentar
-timeoutindefinidamente. Corrija deadlock, I/O sem deadline ou separação inadequada dos testes. - Parsear
-jsoncomo um objeto único. A saída é composta por eventos, um por linha. - Repetir um teste flakey até ele passar. Retry pode reduzir ruído temporariamente, mas não elimina estado compartilhado, relógio instável ou concorrência incorreta.
- Misturar benchmark com testes unitários caros. Use
-run='^$'quando quiser medir apenas benchmarks.
Checklist de diagnóstico
Quando um teste falhar, siga esta ordem:
- Rode o pacote com
-v -count=1. - Isole o nome exato com
-run. - Repita com
-count=20ou mais se houver suspeita de flakiness. - Use
-shuffle=one preserve a seed se a ordem puder influenciar. - Adicione
-racese existir estado compartilhado ou goroutines. - Defina um
-timeoutcurto o bastante para obter stack traces rapidamente. - No CI, salve
-jsone associe a saída ao teste correto. - Corrija isolamento e contrato; não apenas aumente retries e timeouts.
Perguntas frequentes
Como rodar apenas um teste específico com go test?
Use go test -run '^TestNome$' ./caminho/do/pacote. Para subtests, inclua o caminho separado por barra, como go test -run 'TestPedido/sem_estoque' ./internal/pedido.
Como executar go test sem cache?
Use go test -count=1 ./.... Isso força uma execução em vez de reutilizar um resultado bem-sucedido armazenado.
Para que serve go test -shuffle?
Serve para randomizar a ordem dos testes e revelar dependências ocultas. Use -shuffle=on; se falhar, copie a seed exibida e reproduza com -shuffle=NUMERO.
Qual é a diferença entre -v e -json?
-v oferece saída detalhada para uma pessoa ler no terminal. -json emite eventos estruturados para CI, relatórios e parsers.
Quando devo usar go test -race?
Use em código concorrente, caches, workers e serviços com estado compartilhado, preferencialmente também em um job separado do CI. Ele aumenta o custo e só encontra races nos caminhos exercitados.
Próximos passos
- Go Test: guia completo de testes — escrita de testes, mocks, HTTP, integração, cobertura e fuzzing;
- Table-driven tests — organize muitos cenários com
t.Run; - Fuzz testing nativo — descubra entradas que quebram parsers e validadores;
- Testcontainers em Go — teste PostgreSQL, Redis e outros serviços reais;
- Benchmarks com testing.B — meça tempo e alocações sem se enganar;
- Vagas Go no Brasil — encontre equipes que valorizam testes, CI e serviços confiáveis.
Dominar as flags do go test não significa decorar todas as opções. Significa escolher a menor combinação capaz de responder à pergunta atual: qual caso falhou, ele executou de verdade, depende da ordem, é concorrente e como reproduzo no CI?