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
- Nome descritivo —
TestUserCreate_WithInvalidEmail_ReturnsError - Table-driven — Para múltiplos cenários
- Isolamento — Cada teste independente
- Fast — Testes unitários < 1s
- Determinístico — Mesmo resultado sempre
- 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
- API REST com Go — Aplique testes numa API real
- Concorrência em Go — Teste código concorrente
- Interfaces em Go — Use interfaces para mocks
- Go Cheatsheet — Referência rápida de sintaxe