Sumário 🔗
Se você está lendo isso, provavelmente já leu a primeira e a segunda parte deste post. Se não, recomendo que leia a parte 2 antes de continuar.
Como mockar? 🔗
“Accept interface, return struct” é quase que um mantra em Go e pensar dessa forma vai te ajudar na criação de mocks e testes mais flexíveis. Mas afinal o que isso significa?
Vamos imaginar que a função que você precisa testar aceita parâmetros de entrada, como por exemplo uma use case que aceite uma struct de um repositório. Ao tentar criar um teste para essa função você não consegue alterar o comportamento daquele repositório, já que você vai precisar simular operações em banco de dados. Isso se dá ao fato que tipos concretos não podem ser alterados, e para deixar essa função testável, basta você criar uma interface que implemente as funções dessa struct do lado do chamador. Isso significa que você só precisa criar essa interface de acordo com os métodos que você utiliza nessa função, e não precisa se preocupar com todos os métodos da struct original e passar ela como parâmetro para a função que você quer testar. Logo em Go percebemos que não é possível criar mocks diretamente de structs, mas você pode criar mocks de interfaces.
Geradores de mock 🔗
Existem algumas bibliotecas que te ajudam a gerar mocks de uma forma mais automatizada, como por exemplo:
- Mockery
- Mock (Uber)
- Moq
- pgxmock para mocks de banco de dados PostgreSQL
|
|
|
|
O comando acima irá gerar um arquivo chamado mock_twitch_client.go
no mesmo pacote, contendo a implementação do mock da interface TwitchClient
. Você pode então usar esse mock nos seus testes.
Manual 🔗
Para criar um mock manualmente, você deve criar uma struct que implemente a interface que você quer mockar e implementar os métodos dessa interface. Eu gosto de criar uma struct que aceite uma função e tenha um contador por função para posteriormente fazer asserts em cima do número de vezes que ela foi chamada. Vale salientar que essa struct deve ficar dentro do seu arquivo de testes e não de forma exportada em algum pacote que não seja de testes, veja este exemplo:
|
|
Dicas finais 🔗
Como última dica, vale ressaltar que as interfaces devem ser pequenas e específicas, evitando criar interfaces muito genéricas que podem dificultar a compreensão do código e a criação de mocks. Além disso, evite criar mocks de interfaces que não são utilizadas diretamente no seu código, pois isso pode levar a testes desnecessários e complexos.
Por exemplo, se você tem uma interface que define métodos para manipulação de arquivos, mas você só precisa de um método específico para o seu teste, crie uma interface menor que contenha apenas esse método.
Rob Pike já dizia: “The bigger the interface, the weaker the abstraction.”.
“Don’t design your interfaces in advance, discover them as you go.” Não crie interfaces antes dos tipos concretos, descubra-as conforme você vai escrevendo o código. Isso ajuda a manter as interfaces pequenas e específicas, facilitando a criação de mocks e testes mais flexíveis.