---
title: "Go Testing: TDD e Benchmarks - Guia Completo"
url: "https://golang.com.br/tutoriais/go-testing/"
markdown_url: "https://golang.com.br/tutoriais/go-testing.MD"
description: "Aprenda testes em Go com TDD, testes unitários, mock, benchmarks e coverage. Tutorial completo com exemplos práticos para testar aplicações Go profissionalmente."
date: "2026-02-10"
author: ""
---

# Go Testing: TDD e Benchmarks - Guia Completo

Aprenda testes em Go com TDD, testes unitários, mock, benchmarks e coverage. Tutorial completo com exemplos práticos para testar aplicações Go profissionalmente.


Testar código é essencial para garantir qualidade, confiança e manutenibilidade de software. Go possui uma biblioteca de testes robusta e bem integrada na linguagem. Neste guia completo, vamos explorar desde testes unitários básicos até TDD, mocks, benchmarks e testes de integração.

## Por Que Testar em Go?

Go torna testes simples com:

- **Package `testing` embutido** - sem dependências externas
- **Testes compilados** - rápidos e eficientes
- **Coverage nativo** - `go test -cover`
- **Benchmarks** - testes de performance integrados
- **Paralelismo** - `t.Parallel()` para testes concorrentes

## Testes Unitários Básicos

Arquivos de teste terminam com `_test.go`:

```go
// calculator.go
package calculator

func Add(a, b int) int {
    return a + b
}

func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("divisão por zero")
    }
    return a / b, nil
}
```

```go
// calculator_test.go
package calculator

import "testing"

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

func TestDivideByZero(t *testing.T) {
    _, err := Divide(10, 0)
    if err == nil {
        t.Error("esperava erro para divisão por zero")
    }
}
```

Execute com `go test -v` para ver detalhes.

## Table-Driven Tests

O padrão mais idiomático em Go:

```go
func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positivos", 2, 3, 5},
        {"negativos", -2, -3, -5},
        {"zero", 0, 5, 5},
    }

    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; esperado %d",
                    tt.a, tt.b, result, tt.expected)
            }
        })
    }
}
```

## Usando testify

```bash
go get github.com/stretchr/testify
```

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

func TestWithTestify(t *testing.T) {
    // assert continua após falha
    assert.Equal(t, 5, Add(2, 3))
    
    // require para imediatamente
    require.NoError(t, err)
    require.NotNil(t, user)
}
```

### assert vs require

- **`assert`**: Loga erro mas continua o teste
- **`require`**: Para o teste imediatamente na falha

```go
func TestUser(t *testing.T) {
    user, err := CreateUser("joao@example.com")
    
    require.NoError(t, err)      // Para se erro
    require.NotNil(t, user)      // Para se nil
    assert.Equal(t, "João", user.Name)  // Seguro
}
```

## Mocking

### Interface para Mock

```go
type UserRepository interface {
    GetByID(id string) (*User, error)
    Save(user *User) error
}
```

### Hand Mock (Sem Framework)

```go
type MockUserRepository struct {
    GetByIDFunc func(id string) (*User, error)
    SaveFunc    func(user *User) error
}

func (m *MockUserRepository) GetByID(id string) (*User, error) {
    if m.GetByIDFunc != nil {
        return m.GetByIDFunc(id)
    }
    return nil, nil
}
```

### Testando com Mock

```go
func TestService_GetUser(t *testing.T) {
    mock := &MockUserRepository{
        GetByIDFunc: func(id string) (*User, error) {
            return &User{ID: id, Name: "João"}, nil
        },
    }
    
    svc := NewService(mock)
    user, err := svc.GetUser("123")
    
    require.NoError(t, err)
    assert.Equal(t, "João", user.Name)
}
```

## Testes de HTTP

```go
func TestGetUsers(t *testing.T) {
    req, err := http.NewRequest("GET", "/api/users", nil)
    require.NoError(t, err)

    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(getUsersHandler)
    handler.ServeHTTP(rr, req)

    assert.Equal(t, http.StatusOK, rr.Code)
    
    var users []User
    json.Unmarshal(rr.Body.Bytes(), &users)
    assert.Len(t, users, 2)
}
```

## Benchmarks

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

// Com memória
func BenchmarkStringConcat(b *testing.B) {
    b.Run("naive", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            var s string
            for j := 0; j < 100; j++ {
                s += "a"
            }
        }
    })
    
    b.Run("builder", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            var builder strings.Builder
            for j := 0; j < 100; j++ {
                builder.WriteString("a")
            }
        }
    })
}
```

```bash
go test -bench=. -benchmem
```

## TDD - Test Driven Development

Ciclo Red-Green-Refactor:

1. **RED**: Escreva teste que falha
2. **GREEN**: Código mínimo para passar
3. **REFACTOR**: Melhore mantendo testes verdes

```go
// Teste primeiro (falha)
func TestValidator(t *testing.T) {
    v := NewValidator(WithMinLength(8))
    assert.Error(t, v.Validate("curta"))
    assert.NoError(t, v.Validate("senhaok123"))
}

// Implementação mínima
func (v *Validator) Validate(p string) error {
    if len(p) < v.minLength {
        return errors.New("curta")
    }
    return nil
}

// Refatoração
func (v *Validator) Validate(p string) error {
    if len(p) < v.minLength {
        return fmt.Errorf("mínimo %d caracteres", v.minLength)
    }
    return nil
}
```

## Coverage

```bash
# Ver cobertura
go test -cover ./...

# Gerar relatório
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
```

## Comandos Úteis

```bash
# Executar todos os testes
go test ./...

# Verbose
go test -v ./...

# Testes específicos
go test -run TestNome ./...

# Paralelo
go test -parallel 4 ./...

# Tags (testes de integração)
go test -tags=integration ./...
```

## Checklist de Qualidade

- [ ] Cobertura > 80%
- [ ] Table-driven tests
- [ ] Testes para erros
- [ ] Paralelismo onde possível
- [ ] Cleanup com `t.Cleanup()`
- [ ] Benchmarks para código crítico

## Próximos Passos

- [Go para APIs REST](/tutoriais/go-api-rest/) — APIs com testes
- [Go Concurrency](/tutoriais/go-concurrency/) — Testes concorrentes

---

*Testes garantem confiança no código. Invista neles!*

Para expandir seu conhecimento de testes em diferentes ecossistemas, confira como <a href="https://python.dev.br/blog/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">Python</a> aborda testes com pytest e como <a href="https://kotlin.dev.br/blog/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'kotlin.dev.br' })">Kotlin</a> usa JUnit e Kotest para testes expressivos na JVM.
