💻 Exemplos: Capítulo 15: Testes Unitários e de Integração com httptest e Testify
Especialização em Backend com Go & Gin • Demonstrações Práticas Compiláveis
💻 Código de Exemplo 01 — Teste de Handler Gin com httptest e testify/assert
package handlers_test
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
)
func configurarRouterTeste() *gin.Engine {
gin.SetMode(gin.TestMode)
r := gin.New()
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "pong"})
})
return r
}
// TestPingHandler despacha a requisição direto no Engine, sem abrir porta
// TCP: ServeHTTP roda a cadeia de middlewares e o handler em memória.
func TestPingHandler(t *testing.T) {
r := configurarRouterTeste()
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/ping", nil)
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
assert.JSONEq(t, `{"msg":"pong"}`, w.Body.String())
}
💻 Código de Exemplo 02 — Mock de Repositório com testify/mock
package service_test
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
type Produto struct {
ID uint
Preco float64
}
type ProdutoRepository interface {
BuscarPorID(id uint) (*Produto, error)
}
type MockProdutoRepo struct{ mock.Mock }
func (m *MockProdutoRepo) BuscarPorID(id uint) (*Produto, error) {
args := m.Called(id)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*Produto), args.Error(1)
}
// CalcularDescontoVIP é a regra de negócio pura sob teste: 10% de desconto.
func CalcularDescontoVIP(repo ProdutoRepository, id uint) (float64, error) {
p, err := repo.BuscarPorID(id)
if err != nil {
return 0, err
}
return p.Preco * 0.9, nil
}
func TestCalcularDescontoVIP_ProdutoExiste(t *testing.T) {
mockRepo := new(MockProdutoRepo)
mockRepo.On("BuscarPorID", uint(1)).Return(&Produto{ID: 1, Preco: 100.0}, nil)
preco, err := CalcularDescontoVIP(mockRepo, 1)
assert.NoError(t, err)
assert.Equal(t, 90.0, preco)
mockRepo.AssertExpectations(t)
}
func TestCalcularDescontoVIP_ProdutoInexistente(t *testing.T) {
mockRepo := new(MockProdutoRepo)
mockRepo.On("BuscarPorID", uint(99)).Return(nil, errors.New("não encontrado"))
_, err := CalcularDescontoVIP(mockRepo, 99)
assert.Error(t, err)
}
💻 Código de Exemplo 03 — Teste de Integração com SQLite em Memória e Benchmark
package integration_test
import (
"encoding/json"
"testing"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type Produto struct {
ID uint `gorm:"primaryKey"`
Nome string
Preco float64
}
// configurarBancoTeste sobe um SQLite in-memory isolado por processo de
// teste e roda as migrações reais, validando queries e constraints de fato.
func configurarBancoTeste(t *testing.T) *gorm.DB {
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
require.NoError(t, err)
require.NoError(t, db.AutoMigrate(&Produto{}))
return db
}
func TestCriarEBuscarProduto(t *testing.T) {
db := configurarBancoTeste(t)
produto := Produto{Nome: "Monitor", Preco: 899.90}
require.NoError(t, db.Create(&produto).Error)
var encontrado Produto
require.NoError(t, db.First(&encontrado, produto.ID).Error)
require.Equal(t, "Monitor", encontrado.Nome)
}
// BenchmarkJsonSerialization mede ns/op e allocs/op com `go test -bench=. -benchmem`.
func BenchmarkJsonSerialization(b *testing.B) {
p := Produto{ID: 10, Nome: "Benchmark", Preco: 49.9}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = json.Marshal(p)
}
}