Usando WireMock para acelerar Testes Unitários e Integrados com Spring Boot e API Oficial da Marvel
Meu Canal no YouTube
https://www.youtube.com/thomasdacosta
Cenário
No mundo atual de desenvolvimento, muitas vezes é necessário efetuar chamadas HTTP para outros serviços ou endpoints específicos necessários para o funcionamento da nossa aplicação.
Quando trabalhamos com microsserviços, algumas vezes é necessário fazer uma orquestração ou uma coreografia para vários outros microsserviços e essas chamadas podem ser em HTTP ou em gRPC.
Quando trabalhamos em arquiteturas mais complexas, e segmentadas divididas por exemplo por áreas de canais e áreas de produtos vamos precisar efetuar integrações entre sistemas.
Podemos também possuir frontends que acessam backends através de um padrão conhecido como BFF.
Com este cenário, temos um problema quando vamos desenvolver nossos testes unitários: temos dependências externas de outros microsserviços e endpoints dificultando o teste unitário e integrado.
Poderíamos resolver esse cenário com o uso de algum framework de mock de objetos mas podemos acabar ocultando lógicas de negócios ou até mesmo não executando um teste próximo do real.
O que é o WireMock?
Para solucionar o problema acima podemos usar o WireMock.
WireMock é um framework que tem como objetivo, efetuar o mock de um serviço externo em HTTP sendo um Mock Server ou Mock de Servidor, simulando uma chamada com seu respectivo verbo HTTP (GET, POST, PUT e etc), headers, códigos de retorno, response body e outro recursos necessários para montar a chamada HTTP.
Vamos ver a nossa aplicação antes de criar os testes
Antes mostrar o exemplo de código do WireMock, vamos analisar a aplicação que será testada.
A aplicação tem como objetivo ser um BFF para a API Oficial da Marvel.
Com essa API, conseguimos obter informações sobre Personagens, HQ´s, Eventos, Histórias e outras informações sobre o Universo da Marvel.
O BFF desenvolvido tem um endpoint denominado /marvel/heros/{name} que busca um Personagem através do nome e retorna suas características, as ultimas 20 HQ´s e Eventos que participou.
Para conseguir utilizar a API da Marvel é necessário efetuar o cadastro no portal para obter as chaves de acesso para a sua aplicação, BFF ou API:
Será necessário gerar um hash com as chaves da API da Marvel para efetuar as chamadas. Na documentação do Portal da Marvel existe uma explicação:
Depois disso baixe o Swagger do site abaixo:
Com o Swagger em mãos, vamos gerar o client para a API da Marvel e seus respectivos objetos usando a biblioteca Feign Client do Spring Cloud. Essa biblioteca facilita o desenvolvimento de chamadas HTTP externas diminuindo a complexidade do código para chamada de API´s mais complexas.
Não vamos entrar no detalhe do funcionamento do Feign Client nesse artigo
Execute os comandos abaixo para gerar o client e os objetos a partir do Swagger:
wget https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/3.0.29/swagger-codegen-cli-3.0.29.jar -O swagger-codegen-cli.jarjava -jar swagger-codegen-cli.jar generate -i marvel-public-api-v1-swagger.json -l spring --library spring-cloud -o marvel
Acesse o GitHub para ver a aplicação completa:
Analisando o código da aplicação
A aplicação é composta pela camada Controller, Service e Feign Client para chamadas da API da Marvel.
Abaixo estamos efetuando a busca de um Personagem através de um nome especifico e efetuando a transformação das informações para um outro objeto, que será encaminhado para a camada Controller.
A classe MarvelApiClient é referente ao Feign Client que acessa diretamente a API da Marvel:
Além disso, outros dois métodos efetuam a busca das HQ´s e Eventos do personagem encontrado:
Como funciona o WireMock
O WireMock possui uma API fluente para construção dos testes integrados usando a classe WireMock:
Podemos ler o código acima da seguinte forma:
WireMock.stubFor = crie um mock de um servidor para o seguinte verbo HTTP.
WireMock.get = crie um mock para o verbo GET com o endereço especificado.
willReturn = que irá retornar uma resposta HTTP.
WireMock.aResponse = crie uma resposta HTTP.
withStatus = com um HTTP Status definido.
withHeader =com um header de resposta.
withBody = com um corpo de resposta especifico.
Colocando nosso código em uma frase seria a seguinte:
Crie um mock de um servidor usando o verbo GET que irá retornar uma resposta HTTP com status 200 com um header de resposta no formato JSON e gere um body com JSON de resposta do personagem
Código Fonte dos Testes Integrados com o WireMock
A classe de teste deve possuir a anotação @WireMockTest para definir o servidor e a porta que será inicializado.
No nosso exemplo, o servidor utilizará a porta 8081:
O cenário de teste a seguir, foi criado pensando em um retorno com HTTP Status 200 da API:
Para cada teste integrado, devemos definir um application.properties com portas diferentes e nome de arquivos diferentes para não receber um erro de porta em uso.
O erro de porta em uso pode acontecer quando um teste termina e um outro é inicializado quando usamos alguma automação ou quando estamos executando dentro de uma esteira de Devops.
A propriedade marvelPublicAPIV1.url define o endereço onde o Feign Client irá efetuar a conexão com a API da Marvel.
Como estamos utilizando o WireMock, iremos configurar o endereço para localhost com a porta especificada dentro da classe de teste:
Vamos deixar o teste um pouco mais completo criando um cenário que retorna um HTTP Status 404 quando um Personagem não existe:
Classe de teste integrado para a camada de Service:
Classe de teste integrado para a camada de Controller.
Com esse cenário conseguimos efetuar um teste de ponta a ponta dentro da aplicação:
Considerações
- Sempre coloque todos os cenários possíveis dos seus testes integrados
- Sempre teste todos os HTTP Status que a integração possa retornar
- Separe logicamente os seus testes integrados de acordo com as camadas da aplicação
- Independente que o teste seja integrado ou unitário, uma boa prática é sempre ter um application.properties por teste para deixar bem separado as configurações e o teste ser independente
Repositório do GitHub
O código fonte desse artigo está na branch wiremock-test.
Até a próxima!