Hoverfly – Simulando suas dependências – Blog da Globo
Hoverfly – Simulando suas dependências
11 de março de 2019 11 de março de 2019 ~ Evandro Souza
Atualmente, com o advento dos microservices, é muito comum que aplicações dependam de serviços internos ou de terceiros. Por exemplo, um e-commerce pode depender de um serviço de terceiros para processar pagamentos com cartão ou de uma rede social para fornecer autenticação. Esses tipos de aplicações normalmente são desafiadores para serem testados isoladamente, visto que suas dependências podem apresentar problemas como:
Não ser determinístico.
Possuir Builds lentos e custosos.
Mocking complexos.
Altos custos de licença.
Provisionamento lento.
Para resolver isso, a virtualização de serviços pode emular essas dependências. Ao contrário do mocking, que substitui o código da aplicação, a virtualização de serviço ocorre geralmente operando no nível da rede. Não é invasivo e do ponto de vista da aplicação consumidora não há mudanças.
O que é o Hoverfly?
Figura 1 – Hoverfly é escrito em Go
O Hoverfly é uma ferramenta Open-source de virtualização de serviços HTTP que é normalmente usada para simular dependências de API. Escrito em Go, é um proxy de encaminhamento que intercepta, substitui e modifica o tráfego para um serviço externo. Seus dois principais modos são:
Capture mode: O proxy transmite o tráfego HTTP para o servidor remoto normalmente, porém, todo o tráfego HTTP é interceptado e armazenado na memória.
Figura 2 – Modo captura
Simulate mode: Aqui, nenhum tráfego HTTP é realmente intermediado pelo proxy para o servidor real. Em vez disso, um processo chamado matching ocorre para encontrar na memória a resposta apropriada para determinada solicitação.
Figura 3 – Modo simulação
Além destes dois modos — que considero serem os principais — também existem outros quatro, não vou detalha-los aqui para não estender demais o post.
Para apoiar este tutorial, vamos usar um exemplo que localizei no GitHub, desenvolvido pelo Saurabh Badhwar. É uma aplicação de ToDo escrita — de modo muito simplificado — usando arquitetura microservices. Conforme é possível visualizar na Figura 4, a aplicação possui dois serviços, sendo que o serviço "Users" possui uma dependência do serviço "Todo".
Figura 4 — ilustração dos Microserviços.
Para executar este projeto, utilizei o sistema operacional Ubuntu 18 e rodei tudo dentro de um virtual environment de Python.
Instale Python 3, pip e Virtualenv.
sudo apt-get install python3-pip python3-dev python-virtualenv
2. Crie e ative o Virtualenv.
virtualenv --system-site-packages -p python3 .env3
3. Clone o repositório:
cd /minha/pasta/de/projetos/
git clone https://github.com/h4xr/todo
Dica: Faça um fork para o seu github pessoal.
4. Instale as dependências:
5 . Rode o serviço dos usuários.
python services/users.py
6. Abra outro terminal e rode o serviço da lista de todo.
python services/todo.py
7. Verifique se está tudo certo, abra seu browser e acesse:
http://localhost:5000/users/saurabh_badhwar/lists
Caso esteja tudo rodando corretamente deverá aparecer algo como abaixo no seu browser:
{ "home": [ "Buy milk", "Look for pest control service", "Get a new carpet" ], "work": [ "Complete the blogpost", "Create presentation for meeting" ] }
Queremos testar o serviço REST de usuários, o teste que precisamos fazer é para assegurar a entrada e a saída de cada endpoint.
Este teste deve ser isolado, isso quer dizer que ele precisa funcionar independente dos outros serviços (no caso, o serviço Todo).
Contudo, da maneira que está hoje, não conseguimos testar nosso serviço de usuários sem depender que o serviço de Todo esteja rodando junto. Faça um teste, pare o serviço de Todo e acesse o seguinte endpoint:
Deve ocorrer um erro. Neste cenário nosso teste iria falhar devido à indisponibilidade de outro serviço. Seria um falso positivo.
Além desse problema, chamar um serviço real durante testes pode ter ainda outros problemas, por exemplo:
A resposta do serviço pode mudar, sendo assim fica difícil escrever um teste.
Alguns serviços possuem um custo ao chamar com muita frequência. Sendo assim, é importante manter as suas chamadas no mínimo possível.
Hoverfly entra em cena
Para conseguir testar nosso serviço de modo apropriado, vamos utilizar o Hoverfly.
A instalação dele é muito simples. Baixe o arquivo zip e descompacte em alguma pasta. No nosso exemplo vamos descompactar na pasta ~/hoverfly .
Dentro dessa pasta terá dois binários:
hoverfly: É o aplicativo que faz a maior parte do trabalho. Ele fornece o servidor proxy ou o servidor da Web e os pontos de extremidade da API.
hoverctl: É um comando de linha que pode ser usada para configurar e controlar o Hoverfly.
Para testar se está tudo funcionando, acesse a pasta do Hoverfly e digite:
$ ./hoverctl start
Hoverfly is now running
| admin-port | 8888 |
| proxy-port | 8500 |
+------------+------+
Isso gerará dois processos: o proxy central do Hoverfly e a interface administrativa. Também podemos rodar um comando para verificar o status desses processos e como o proxy está operando:
$ ./hoverctl status
| Hoverfly   | running  |
| Admin port |     8888 |
| Proxy port |     8500 |
| Proxy type | forward  |
| Mode       | simulate |
| Middleware | disabled |
É possível notar que atualmente o proxy está em modo de simulação. Para começar a testar nossa aplicação, vamos colocar ele em modo captura.
$ ./hoverctl mode captureHoverfly has been set to capture mode
Pronto, a partir de agora, toda request que fizermos usando o proxy do Hoverfly será capturada.
Configurando o proxy na aplicação
No projeto clonado, abra o arquivo services/users.py e localize a função user_lists. Este é o ponto que o serviço Users chama o serviço Todo. É esta comunicação que queremos simular. Modifique o código para usar o proxy:
def user_lists(username):
''' Get lists based on username '''
'http': 'http://127.0.0.1:8500/',
"http://127.0.0.1:5001/lists/{}".format(username), proxies=proxies)
return str(ex.message)
Agora, com ambos serviços rodando no terminal, volte ao browser e acesse novamente o endpoint:
Como está configurado em modo captura, o serviço Todo foi chamado normalmente. Para comprovar isso, digite no terminal do hoverctl:
$ ./hoverctl logs
INFO[2019-01-27T14:48:26-02:00] request and response captured                 mode=capture request="&map[headers:map[Connection:[keep-alive] Accept-Encoding:[gzip, deflate] Accept:[*/*] User-Agent:[python-requests/2.11.1]] body: method:GET scheme:http destination:127.0.0.1:5001 path:/lists/saurabh_badhwar query:map[]]" response="&map[error:nil response]"
Agora, vamos testar se está tudo funcionando conforme o esperado:
Pare somente o serviço de Todo.
Mude o HoverFly para o modo de simulação
$ ./hoverctl mode simulate
3. Acesse novamente o endpoint e perceba que está funcionando normalmente, mesmo o serviço Todo estando desligado.
Note que no momento que o Hoverfly for desligado (hoverctl stop), os dados capturados são perdidos. É possível exportar os dados capturados para reutilizá-los quando necessário:
$ ./hoverctl export simulation.json
E para utilizar os dados:
Ao rodar o comando acima, o Hoverfly automaticamente entra em modo de simulação.
Neste post vimos um exemplo simples de gravação e reprodução de um endpoint com o HoverFly. Ele nos permitiu substituir uma dependência de teste não confiável por uma simulação determinística confiável.
É importante citar que — por ser um texto de caráter introdutório — muitos conceitos avançados foram evitados. Por exemplo: