Go é uma linguagem de programação poderosa e escalável, mas como qualquer outra linguagem, é importante garantir que o código seja testado de maneira eficaz e confiável. Uma técnica comum de teste é o Monkey Patching, que consiste em modificar o comportamento de uma função ou método já existente sem modificar sua implementação original. No entanto, essa técnica pode trazer problemas de confiabilidade e manutenção no seu código.
Primeiramente, o Monkey Patching aumenta a complexidade do código, já que é necessário garantir que as modificações não afetem outras partes do sistema. Além disso, pode haver problemas de compatibilidade com outras bibliotecas ou pacotes que dependem do comportamento original. E finalmente, pode haver problemas de testabilidade, já que é necessário garantir que as modificações não afetem os testes de outras partes do sistema.
O que pode dar de errado?
Imagine que você tem uma função “ProcessarPagamento” que chama uma outra função “ValidarCartão” para validar o cartão de crédito do cliente antes de processar o pagamento. Durante os testes, você decide usar monkey patching para substituir a função “ValidarCartão” por uma função mock. Isso funciona bem no seu ambiente de teste, mas quando o código é colocado em produção, a função “ValidarCartão” original é chamada e o pagamento não é processado corretamente. Isso acontece porque o monkey patching foi aplicado apenas no seu ambiente de teste e não foi considerado no ambiente de produção.
Esse é um exemplo de como o uso de monkey patching pode causar problemas de confiabilidade e manutenção no seu código Go. É importante utilizar ferramentas confiáveis e testar de forma robusta para garantir a qualidade e segurança do seu software.
Alguns tipos de implementações para Mocks
Testes são uma parte fundamental do desenvolvimento de software e permitem que os desenvolvedores verifiquem se o código funciona como o esperado. No entanto, quando se trata de testes unitários, muitas vezes é necessário mockar certos componentes ou dependências para garantir que o teste seja independente e repetível. É aqui que entram em cena os mocks.
Os mocks são uma representação simulada de uma dependência ou componente que permite aos desenvolvedores controlar e prever o comportamento de certas partes do código durante os testes. No entanto, existem várias formas de criar mocks, incluindo o uso de bibliotecas de terceiros, como mockery e testify, gomock, ginkgo ou a criação de interface fakes manualmente.
Neste artigo, exploraremos a vantagem de usar mocks com cada uma dessas bibliotecas em vez de monkey patch, dando exemplos de implementação e mostrando por que essas abordagens são preferíveis ao uso do monkey patch.
Vejamos abaixo exemplos de implementação de testes utilizando as tecnicas citadas acima, vamos mockar o coportamente de um banco de dados.
Inteface Fakes
Interface Fakes: Permite a criação de objetos falsos que implementam uma interface, utilizados para fins de teste.
package main
import (
"testing"
)
type DB interface {
Query(query string) ([]byte, error)
}
type DBTest struct{}
func (db *DBTest) Query(query string) ([]byte, error) {
if query == "SELECT * FROM users" {
return []byte("fake data"), nil
}
return nil, nil
}
func TestMain(t *testing.T) {
// Gerando o mock
mockDB := &DBTest{}
// Usando o mock no código
data, _ := mockDB.Query("SELECT * FROM users")
if string(data) != "fake data" {
t.Errorf("Dados devem ser iguais a 'fake data', mas recebidos '%s'", string(data))
}
}
Testify
Github: https://github.com/stretchr/testify
Testify é uma biblioteca para Go que oferece recursos para testes de unidade, incluindo assertivas, mocks e outras funcionalidades para ajudar a desenvolver testes de unidade de forma eficiente. É uma das bibliotecas mais usadas para testes em Go.
package main
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
type DB interface {
Query(query string) ([]byte, error)
}
type DBTest struct {
mock.Mock
}
func (db *DBTest) Query(query string) ([]byte, error) {
args := db.Called(query)
return args.Get(0).([]byte), args.Error(1)
}
func TestMain(t *testing.T) {
// Gerando o mock
mockDB := new(DBTest)
mockDB.On("Query", "SELECT * FROM users").Return([]byte("fake data"), nil)
// Usando o mock no código
data, _ := mockDB.Query("SELECT * FROM users")
assert.Equal(t, []byte("fake data"), data, "Dados devem ser iguais a 'fake data'")
}
Monkery
Github: https://github.com/vektra/mockery
Mockery é uma biblioteca para gerar código de mocks em Go. É fácil de usar e oferece uma interface amigável para gerar mocks para suas interfaces e estruturas. Ele também oferece recursos para personalização de mocks e integração com outras bibliotecas de testes.
package main
import (
"fmt"
"testing"
"github.com/vektra/mockery/mockery"
)
type DB interface {
Query(query string) ([]byte, error)
}
func TestMain(t *testing.T) {
// Gerando o mock
generator, err := mockery.NewGenerator()
if err != nil {
t.Fatalf("Não foi possível criar o gerador de mock: %s", err)
}
err = generator.Start("./mocks")
if err != nil {
t.Fatalf("Não foi possível iniciar o gerador de mock: %s", err)
}
err = generator.Generate("DB")
if err != nil {
t.Fatalf("Não foi possível gerar o mock: %s", err)
}
mockDB := new(mocks.DB)
mockDB.On("Query", "SELECT * FROM users").Return([]byte("fake data"), nil)
// Usando o mock no código
data, _ := mockDB.Query("SELECT * FROM users")
fmt.Println(string(data))
}
Note que o código acima gera um arquivo de mock mocks/DB.go
que contém a implementação do mock para a interface DB
. Em seguida, esse mock é usado no teste para simular o comportamento da interface DB
.
Gomock
Github: https://github.com/golang/mock
Gomock é uma biblioteca de mock gerada automaticamente que permite criar mocks de interface rapidamente. Ele fornece uma sintaxe simples e intuitiva para definir expectativas de chamadas e verificar se foram atendidas.
package main
import (
"testing"
"github.com/golang/mock/gomock"
)
type DB interface {
Query(query string) ([]byte, error)
}
func TestMain(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
// Gerando o mock
mockDB := NewMockDB(ctrl)
mockDB.EXPECT().Query("SELECT * FROM users").Return([]byte("fake data"), nil)
// Usando o mock no código
data, _ := mockDB.Query("SELECT * FROM users")
if string(data) != "fake data" {
t.Errorf("Dados devem ser iguais a 'fake data', mas recebidos '%s'", string(data))
}
}
Ginkgo
Github: https://github.com/onsi/ginkgo
Ginkgo é uma biblioteca de testes BDD que fornece uma sintaxe fluente e uma ampla gama de recursos para escrever testes em Go. Ele inclui suporte integrado para mocks e permite a criação de mocks facilmente usando a funcionalidade de “spies”.
package main
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type DB interface {
Query(query string) ([]byte, error)
}
type DBTest struct{}
func (db *DBTest) Query(query string) ([]byte, error) {
if query == "SELECT * FROM users" {
return []byte("fake data"), nil
}
return nil, nil
}
var _ = Describe("TestMain", func() {
It("Deve retornar os dados fake corretos", func() {
// Gerando o mock
mockDB := &DBTest{}
// Usando o mock no código
data, _ := mockDB.Query("SELECT * FROM users")
Expect(string(data)).To(Equal("fake data"))
})
})
Em geral, todas as bibliotecas acima são úteis para criar mocks de objetos e interfaces em Go, mas a escolha depende das necessidades específicas de cada projeto. Algumas pessoas preferem Ginkgo por sua integração fácil com testes BDD, enquanto outras preferem Gomock por sua facilidade de uso e geração automática de código. Mocket é uma boa escolha para quem procura uma solução simples e rápida para criar mocks.
Conclusão
Em resumo, o uso de mocks é fundamental em testes de software para simular comportamentos de dependências externas. No entanto, é importante escolher a abordagem correta de mock para garantir a qualidade e a confiabilidade dos testes.
O monkey patching pode ser uma técnica fácil e rápida, mas não é recomendado devido aos problemas de manutenção e a possibilidade de interferir em outros testes. Em vez disso, é melhor usar ferramentas como vektra/mockery, testify ou interface fakes, que oferecem recursos avançados de mock e garantem uma abordagem mais profissional e segura para a criação de mocks.
Portanto, sempre que precisar de mocks em seus testes, considere usar as melhores práticas e ferramentas para garantir a qualidade e confiabilidade do seu código.
Github: https://github.com/phelliperodrigues/mock-examples-golang