Quando a realidade atrapalha: por que precisamos de mocks e fixtures em Go?
\n\nImagine que você está testando uma função que envia uma notificação por e-mail. Cada execução do teste é um e-mail real na caixa de entrada. Após dez testes, sua caixa está cheia e seus colegas amaldiçoam o spam. Ou pior: a função acessa uma API externa que é paga ou funciona uma vez por dia.
\n\nNo mundo Go (ou Golang, como é frequentemente chamado) testar não é apenas marcar uma caixa na lista de verificação. É a base da confiabilidade. Mas para que os testes sejam rápidos e independentes, precisamos isolar o código do mundo externo. É aqui que entram em cena os objetos mock (mocks) e as fixtures.
\n\nNeste artigo, vamos explorar como substituir dependências (mockar) e preparar dados (fixtures) corretamente em Go. Você aprenderá a escrever testes que não falham devido à rede, banco de dados ou hora do dia. Vamos lá!
\n\nO que são Mock e Fixture no contexto do Go?
\n\nObjetos Mock: dublês para sistemas complexos
\n\nObjetos Mock são "substitutos" de software que imitam o comportamento de componentes reais. Em vez de chamar um banco de dados real ou serviço HTTP, inserimos um objeto que retorna respostas predefinidas.
\n\nEm Go, os mocks são especialmente importantes porque a linguagem incentiva o trabalho com interfaces. Se seu código depende de uma interface, você pode facilmente substituir a implementação por uma de teste.
\n\nFixtures: preparando o cenário
\n\nFixtures são dados ou estados que devem ser estabelecidos antes do início do teste. Por exemplo, criar um banco de dados temporário, preenchê-lo com registros de teste ou configurar o sistema de arquivos.
\n\nAo contrário dos mocks, que substituem o comportamento, as fixtures preparam o ambiente. Elas respondem à pergunta: "O que precisa estar pronto para o teste começar?"
\n\nEscrevendo mocks em Go: do simples ao complexo
\n\nA maneira mais simples: implementação manual da interface
\n\nGo não requer bibliotecas especiais para criar mocks. Se você tem uma interface, pode escrever sua implementação de teste manualmente. Isso é ideal para projetos pequenos.
\n\nSuponha que temos uma interface para envio de notificações:
\n\npackage notifier\n\ntype Notifier interface {\n Send(to string, message string) error\n}\n\n// Implementação real (não para testes)\ntype EmailNotifier struct{}\n\nfunc (e *EmailNotifier) Send(to string, message string) error {\n // Envio de e-mail real\n return nil\n}\n\nNo arquivo de teste, criamos um mock:
\n\npackage notifier\n\nimport "testing"\n\ntype MockNotifier struct {\n Called bool\n LastTo string\n LastMsg string\n}\n\nfunc (m *MockNotifier) Send(to string, msg string) error {\n m.Called = true\n m.LastTo = to\n m.LastMsg = msg\n return nil\n}\n\nfunc TestSendAlert(t *testing.T) {\n mock := &MockNotifier{}\n // Usamos o mock em uma função que aceita Notifier\n SendAlert(mock, "user@test.com", "Hello!")\n \n if !mock.Called {\n t.Error("O método Send não foi chamado")\n }\n if mock.LastTo != "user@test.com" {\n t.Errorf("Destinatário incorreto: %s", mock.LastTo)\n }\n}\n\nVantagens: controle total, nenhuma dependência.
\nDesvantagens: muito código repetitivo para interfaces complexas.
Usando testify/mock: poderoso e conveniente
\n\nA biblioteca testify é o padrão de facto para mocks em Go. Ela fornece um framework flexível para criar mocks com verificação de argumentos e número de chamadas.
Instalação:
\ngo get github.com/stretchr/testify\n\nExemplo usando testify/mock:
\n\npackage user\n\nimport (\n "testing"\n "github.com/stretchr/testify/mock"\n)\n\n// Definimos a interface\ntype UserRepository interface {\n GetUser(id int) (string, error)\n}\n\n// Criamos a estrutura mock\ntype