Como executar todos os testes em um projeto Go e o que acontece "por baixo dos panos"?
\nTodo desenvolvedor Go já digitou pelo menos uma vez no terminal o comando mágico go test ./... e observou as marcas verdes correndo pela tela. Mas você já parou para pensar no que realmente acontece nesse momento? Por que os testes são executados exatamente dessa forma e não de outra? E por que, às vezes, o go test silenciosamente ignora seus novos testes?
Neste artigo, não vamos apenas responder a uma pergunta popular do Stack Overflow (onde ela acumulou milhares de visualizações), mas vamos olhar por baixo dos panos do compilador Go e descobrir como o mecanismo de testes funciona. Vamos nessa!
\n\nPor que go test ./... é mágico?
\nVamos começar pelo básico. O comando go test sem argumentos executa testes apenas no pacote atual. Mas se você tem um projeto com dezenas de pacotes (e em produção real é assim), percorrê-los manualmente é como uma tortura medieval. É aí que entra o ./....
Esse padrão significa: "encontre todos os pacotes recursivamente, a partir do diretório atual". O Go percorre a árvore de diretórios, ignorando vendor e módulos, e coleta uma lista de todos os pacotes. Para cada um deles, ele executa uma instância separada do go test.
Mas e se você quiser executar testes apenas em diretórios específicos? Por exemplo, apenas em ./internal/...? Funciona da mesma forma: go test ./internal/....
Importante: se você usa Go Modules (e a partir do Go 1.16+ isso é padrão), o ./... funciona apenas dentro do módulo atual. Para testar todos os módulos em um workspace, você precisará usar go test ./... em cada um deles ou configurar o go.work.
O que é um "pacote" no contexto do go test?
\nO Go opera com o conceito de pacote — um grupo de arquivos .go em um mesmo diretório. Arquivos de teste (com o sufixo _test.go) também são considerados parte do pacote, mas são compilados separadamente. Quando você executa go test, o Go compila um binário de teste que inclui:
- \n
- O código-fonte do pacote (mas apenas o que é usado nos testes) \n
- O código dos próprios testes \n
- Uma estrutura de runtime especial para execução e relatório \n
Esse binário é executado, realiza os testes e exibe o resultado. Todo o processo é separado para cada pacote.
\n\nComo o Go decide quais testes executar?
\nQuando você digita go test ./..., o Go executa as seguintes etapas:
- \n
- Varredura do sistema de arquivos — o Go encontra todos os diretórios que contêm arquivos
.go. \n - Filtragem — são ignorados os diretórios
vendor,testdatae pastas ocultas (que começam com.). \n - Compilação — para cada pacote, é criado um binário temporário. \n
- Execução — o binário é executado e os resultados são coletados. \n
- Cache — se o código não mudou, o Go usa o cache e exibe
(cached). \n
O último ponto é uma causa frequente de surpresa. Você alterou o teste, executa go test ./..., e ele diz "ok (cached)". Por quê? Porque o Go armazena em cache os resultados com base no hash dos arquivos de entrada. Se você alterou apenas um comentário ou uma importação não utilizada, o hash não muda e o teste não é reexecutado.
Dica: para forçar a limpeza do cache, use o comando go clean -testcache. Ou use a flag -count=1, que faz o Go ignorar o cache: go test -count=1 ./....
Paralelismo: por que os testes nem sempre são mais rápidos?
\nPor padrão, o go test executa testes dentro de um mesmo pacote sequencialmente. Mas entre pacotes — em paralelo! O número de pacotes simultâneos é determinado pela flag -p (por padrão, igual ao número de CPUs).
Isso parece ótimo, mas há um detalhe: e