Testes em Go: unit, integração, e2e em nível avançado

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que testes avançados são um must have em Go



Go (Golang) é famoso por sua simplicidade e ferramentas integradas para testes. No entanto, quando o projeto cresce além do "Hello, World", o padrão go test deixa de ser suficiente. Em nível avançado, os desenvolvedores enfrentam questões: como isolar testes corretamente, como testar a interação com o banco de dados, como simular serviços externos e como organizar uma verificação ponta a ponta de toda a aplicação.



Neste artigo, abordaremos três níveis de teste — Unit, Integration e E2E — com exemplos da prática real. Você aprenderá a usar mocks, contêineres para testes de integração e como construir um pipeline confiável para CI/CD. O material é voltado para desenvolvedores que já conhecem os fundamentos do Go e desejam elevar a qualidade do seu código a um novo patamar.



1. Testes unitários: isolamento, mocks e o padrão Table-Driven Tests



Testes unitários verificam o funcionamento de um único módulo (função, método) em completo isolamento de dependências externas. Em Go, para isso, frequentemente se usam interfaces e bibliotecas de mock, como testify/mock ou gomock.



1.1. Interfaces como base para testes



Para substituir um serviço real por um mock, o código deve depender de uma interface, e não de uma implementação concreta. Vejamos um exemplo de um serviço para envio de notificações.



// notifier.gotype Notifier interface {    Send(to string, message string) error}

type UserService struct { Notifier Notifier}

func (s *UserService) NotifyUser(userID string) error { // lógica para obter o email do usuário email := userID + "@example.com" return s.Notifier.Send(email, "Welcome!") }


1.2. Criando um mock com testify/mock



Instale a biblioteca: go get github.com/stretchr/testify.



// notifier_mock.go (no pacote user_test)type NotifierMock struct {    mock.Mock}

func (m *NotifierMock) Send(to string, message string) error { args := m.Called(to, message) return args.Error(0)}


1.3. Table-Driven Tests



Este é o padrão padrão em Go para testar múltiplos casos. Ele melhora a legibilidade e simplifica a adição de novos testes.



// user_service_test.gofunc TestNotifyUser(t *testing.T) {    tests := []struct {        name      string        userID    string        mockError error        expectErr bool    }{        {"success", "user1", nil, false},        {"fail_send", "user2", errors.New("send error"), true},    }

for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockNotifier := new(NotifierMock) service := &UserService{Notifier: mockNotifier}

mockNotifier.On("Send", tt.userID+"@example.com", "Welcome!").Return(tt.mockError)

err := service.NotifyUser(tt.userID) if tt.expectErr { assert.Error(t, err) } else { assert.NoError(t, err) } mockNotifier.AssertExpectations(t) }) }}


Best practice: Use t.Run para subtestes — isso permite executar cada caso separadamente e ver qual deles falhou.



2. Testes de integração: trabalho com BD e serviços externos



Testes de integração verificam a interação de vários componentes: código Go com banco de dados, servidor API com cache, etc. O principal desafio é gerenciar o estado do ambiente. É aqui que entram os Testcontainers.



2.1. Testcontainers para Go



Testcontainers permitem iniciar contêineres Docker diretamente dos testes. Isso fornece um ambiente limpo e isolado para cada execução.



Instalação: go get github.com/testcontainers/testcontainers-go



2.2. Exemplo: testando

Blogs

Book Recommendations