Testes em Go: Guia Completo

Go tem testing integrado na linguagem. Sem frameworks externos, sem configuração complicada.

Básico

Crie um arquivo *_test.go:

// math.go
package math

func Add(a, b int) int {
    return a + b
}
// math_test.go
package math

import "testing"

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    if result != 5 {
        t.Errorf("Add(2, 3) = %d; want 5", result)
    }
}

Execute:

go test

Table-Driven Tests

O padrão mais usado em Go:

func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positivos", 2, 3, 5},
        {"negativos", -1, -1, -2},
        {"zero", 0, 0, 0},
        {"misto", -1, 5, 4},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Add(%d, %d) = %d; want %d",
                    tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

Saída:

=== RUN   TestAdd
=== RUN   TestAdd/positivos
=== RUN   TestAdd/negativos
=== RUN   TestAdd/zero
=== RUN   TestAdd/misto
--- PASS: TestAdd (0.00s)

Subtests e t.Run

func TestUser(t *testing.T) {
    t.Run("Create", func(t *testing.T) {
        // teste de criação
    })
    
    t.Run("Update", func(t *testing.T) {
        // teste de atualização
    })
    
    t.Run("Delete", func(t *testing.T) {
        // teste de deleção
    })
}

Execute teste específico:

go test -run TestUser/Create

Setup e Teardown

Por teste

func TestDatabase(t *testing.T) {
    // Setup
    db := setupTestDB()
    defer db.Close()
    
    // Teste
    // ...
}

Global (TestMain)

func TestMain(m *testing.M) {
    // Setup global
    db := setupTestDB()
    
    // Rodar todos os testes
    code := m.Run()
    
    // Teardown global
    db.Close()
    
    os.Exit(code)
}

Benchmarks

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(2, 3)
    }
}

Execute:

go test -bench=.

Saída:

BenchmarkAdd-8    1000000000    0.29 ns/op

Benchmark com setup

func BenchmarkSort(b *testing.B) {
    data := generateData(10000)
    
    b.ResetTimer() // Não conta o setup
    
    for i := 0; i < b.N; i++ {
        sort.Ints(data)
    }
}

Coverage

# Ver coverage
go test -cover

# Gerar HTML
go test -coverprofile=coverage.out
go tool cover -html=coverage.out

Mocking

Interface-based

// Interface
type UserRepository interface {
    GetByID(id int) (*User, error)
}

// Mock
type MockUserRepo struct {
    users map[int]*User
}

func (m *MockUserRepo) GetByID(id int) (*User, error) {
    if user, ok := m.users[id]; ok {
        return user, nil
    }
    return nil, errors.New("not found")
}

// Teste
func TestUserService(t *testing.T) {
    mock := &MockUserRepo{
        users: map[int]*User{
            1: {ID: 1, Name: "Alice"},
        },
    }
    
    service := NewUserService(mock)
    user, err := service.GetUser(1)
    
    if err != nil {
        t.Fatal(err)
    }
    if user.Name != "Alice" {
        t.Errorf("expected Alice, got %s", user.Name)
    }
}

Com testify/mock

go get github.com/stretchr/testify
type MockRepo struct {
    mock.Mock
}

func (m *MockRepo) GetByID(id int) (*User, error) {
    args := m.Called(id)
    return args.Get(0).(*User), args.Error(1)
}

func TestWithTestify(t *testing.T) {
    mockRepo := new(MockRepo)
    mockRepo.On("GetByID", 1).Return(&User{Name: "Alice"}, nil)
    
    service := NewService(mockRepo)
    user, _ := service.GetUser(1)
    
    assert.Equal(t, "Alice", user.Name)
    mockRepo.AssertExpectations(t)
}

HTTP Testing

func TestHandler(t *testing.T) {
    // Criar request
    req := httptest.NewRequest("GET", "/users/1", nil)
    rec := httptest.NewRecorder()
    
    // Chamar handler
    handler := http.HandlerFunc(GetUserHandler)
    handler.ServeHTTP(rec, req)
    
    // Verificar
    if rec.Code != http.StatusOK {
        t.Errorf("status = %d; want %d", rec.Code, http.StatusOK)
    }
    
    var user User
    json.NewDecoder(rec.Body).Decode(&user)
    if user.ID != 1 {
        t.Errorf("user.ID = %d; want 1", user.ID)
    }
}

Servidor de teste

func TestAPI(t *testing.T) {
    srv := httptest.NewServer(http.HandlerFunc(myHandler))
    defer srv.Close()
    
    resp, err := http.Get(srv.URL + "/endpoint")
    // ...
}

Testes Paralelos

func TestParallel(t *testing.T) {
    t.Parallel() // Marca como paralelo
    
    // teste...
}
go test -parallel 4

Comandos Úteis

# Rodar todos os testes
go test ./...

# Verbose
go test -v

# Teste específico
go test -run TestAdd

# Com race detection
go test -race

# Timeout
go test -timeout 30s

# Cache off
go test -count=1

Estrutura de Projeto

project/
├── user.go
├── user_test.go
├── user_integration_test.go
└── testdata/
    └── fixtures.json

Build Tags

//go:build integration

package mypackage

func TestIntegration(t *testing.T) {
    // Só roda com: go test -tags=integration
}

Boas Práticas

  1. Nome descritivoTestUserCreate_WithInvalidEmail_ReturnsError
  2. Table-driven — Para múltiplos cenários
  3. Isolamento — Cada teste independente
  4. Fast — Testes unitários < 1s
  5. Determinístico — Mesmo resultado sempre
  6. Coverage > 70% — Meta razoável

Exemplo Completo

// user_service_test.go
package user

import (
    "testing"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

func TestUserService_Create(t *testing.T) {
    tests := []struct {
        name    string
        input   CreateUserInput
        wantErr bool
    }{
        {
            name:    "valid user",
            input:   CreateUserInput{Name: "Alice", Email: "alice@test.com"},
            wantErr: false,
        },
        {
            name:    "empty name",
            input:   CreateUserInput{Name: "", Email: "test@test.com"},
            wantErr: true,
        },
        {
            name:    "invalid email",
            input:   CreateUserInput{Name: "Bob", Email: "invalid"},
            wantErr: true,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            service := NewUserService(NewMockRepo())
            
            user, err := service.Create(tt.input)
            
            if tt.wantErr {
                require.Error(t, err)
                return
            }
            
            require.NoError(t, err)
            assert.NotZero(t, user.ID)
            assert.Equal(t, tt.input.Name, user.Name)
        })
    }
}

Veja Também


Última atualização: Janeiro 2026


Veja também