Introdução aos Testes de Contrato com Pact

Eduardo Pacheco Celeste
beyondTest
Published in
6 min readApr 21, 2020

Uma breve descrição do porque utilizar e quais são seus reais benefícios

API Handshake with CDCT
API Handshake with CDCT

Introdução

A abordagem no uso de microservices trouxe inúmeros benefícios ao desenvolvimento de software nos últimos anos. Nesta arquitetura, softwares robustos foram quebrados em pequenos e independentes componentes que se comunicam entre si através de diferentes protocolos. Dessa maneira o desenvolvimento se tornou mais ágil, escalável e distribuído.

Com os microservices cada componente possui suas próprias responsabilidades, regras e peculiaridades. Nada impede dos times utilizarem stacks distintas dentro do seu contexto assim como normalmente a infraestrutura também não é algo uniforme e muda de time para time. Dessa forma estes componentes podem e devem ser testados e deployados de forma totalmente independente.

Entretanto, apesar dos inúmeros benefícios quando comparado à arquitetura monolítica, inúmeros desafios ainda precisam ser mitigados e um deles é relacionado aos testes.

Os testes de contrato (ou CDCT-Consumer-Driven Contract Testing) são uma ótima alternativa aos testes end-to-end. Além de serem mais rápidos e menos propensos a erros, os testes de contrato são imunes a necessidade de um ambiente estável e integrado entre todos os microservices para só assim poderem ser executados.

É óbvio que não estou dizendo que os testes end-to-end não são importantes ou que são menos essenciais que os de contrato. Tudo depende! A ideia é serem complementares e se possível andarem juntos pois cada um tem suas responsabilidades e a junção destes e outros tipos de teste vão acarretar em um produto sendo entregue com uma melhor cobertura e menos propenso a erros para o usuário final.

Mas como eu disse: depende

Depende pois em uma estrutura simples, com poucos microservices, as vezes adicionar mais um layer só vai aumentar a complexidade na entrega de uma feature. E nesse caso apenas os testes end-to-end são suficientes. Agora imaginem trabalhar em uma arquitetura com dezenas, centenas ou milhares de services. Como garantir que mudanças feitas por um determinado serviço não vão afetar os outros que o consomem?

Microservices DeathStar
Microservices DeathStar

Com base nisso vamos falar um pouco sobre a principal ferramenta quando o assunto são os testes de contrato e entender sobre o seu funcionamento de uma forma conceitual: Pact

Conceitos e terminologia

O site do Pact traz a seguinte analogia:

Do you set your house on fire to test your smoke alarm? No, you test the contract it holds with your ears by using the testing button.

Traduzindo: você incendeia a sua casa pra testar seu alarme de incêndio? Ou você testa o botão que dispara o alarme pra ver se é possível ouvi-lo?

De uma forma resumida e conceitual é isso que o teste de contrato realiza: A comunicação entre duas partes pra garantir que o contrato firmado entre elas continue funcional.

Agora vamos nos aprofundar um pouco mais em suas terminologias pra entendermos de fato seus principais atores. Pra efeito explicativo, vamos imaginar um cenário onde uma aplicação web precisa consumir os dados de uma API:

  • Consumidor (Consumer): No nosso exemplo o consumidor vai ser uma aplicação Web que irá chamar via HTTP uma API pra assim apresentar ou realizar alguma ação. Por exemplo, consumir a API de login do backend.
  • Provedor (Provider): O provedor seguindo nosso exemplo será a API. O conceito do Provider é fornecer os dados esperados pela aplicação Web (seguindo um contrato esperado pelo Consumer) para assim o usuário acessar o sistema.
  • Interação (Interaction): Na interação é onde estará definido como e qual será a funcionalidade a ser consumida. Por exemplo o método e o path da request. Além do formato de resposta esperada pelo Provedor. Exemplificando, a interação será um POST pra API de login (/login) e o body da resposta deverá conter um token de autenticação.
  • Contrato (Contract / Pact file): É o arquivo gerado que contém o contrato entre Consumidor e Provedor. O formato do arquivo normalmente é escrito em JSON e nele irão conter as informações básicas como metadados, estrutura da resposta, status code, etc. Para o nosso exemplo, o contrato poderia conter o status code 200 e a estrutura do body da resposta.
  • Verificação (Verification): Etapa na qual é realizado a validação do pacto. A interação definida no Contrato (Pact File) é confrontado contra o Provider e a resposta e o status code são comparados com o esperado. O resultado da verificação deve ser comunicado ao time do Consumer de alguma forma.
  • Pact Broker: é o local onde serão armazenados os contratos. Não é obrigatório o seu uso mas é totalmente recomendável. Existem soluções para utilizar o Pact Broker via Docker ou através de uma solução SaaS escrita pela própria Pact (Pactflow).

É sempre bom ressaltar que uma aplicação pode ser Consumidor e Provedor. Normalmente o frontend sempre será um consumidor porém há casos na qual podem ser tratados como provedores (Por exemplo quando trabalham com WebSockets (Iremos falar sobre websockets em um post futuro)).

Outro ponto a se comentar é de que não se faz necessário o consumer e o provider serem escritos na mesma linguagem para que um pacto seja firmado entre ambos. Atualmente existe uma convenção que define o formato/extensão do Pact File fazendo com que assim este seja funcional independente da linguagem de programação utilizada. Esta convenção já foi adotada por diversas linguagens de programação como por exemplo o Ruby, JavaScript, Go, Python, Swift, PHP e disponível para a JVM e.NET também. Mais informações podem ser encontradas através deste link.

Funcionamento

Os testes de contrato são mais baratos do que os testes end-to-end por não haver a necessidade de ambientes integrados para que assim sejam executados. Mas como isso funciona?!

A primeira etapa do processo é o de escrever os testes no lado do Consumer. Este teste deverá ser escrito com base em um Mock Service do Provedor (Caso não saibam o que seja um Mock Service, eu escrevi um outro artigo que explica e demonstra seu funcionamento, segue o link). Dessa maneira o Pact File será gerado e enviado para o Pact Broker.

Agora é papel do Provider verificar o contrato criado, seguindo o mesmo fluxo acima, e uma vez validado o pacto é estabelecido entre ambos.

CDCT Flow

Dessa forma temos um acordo entre Consumidor e Provedor. Uma vez que o Provedor realize alguma mudança em seu contrato (por exemplo mudar o tipo do campo id de string para number), o consumidor tem de ser notificado (por exemplo através de webhooks) e o build/release do Provider só poderá seguir adiante mediante acordo entre Provider/Consumers. Em casos mais abruptos a release deverá ser interrompida até que todos os services que consomem o Provider façam a alteração necessária para que a comunicação entre ambos não deixem de funcionar em produção e assim evitem o famoso breaking change.

Conclusão e próximos passos

Como pudemos verificar o Pact é uma ferramenta que contribui consideravelmente na entrega de um software com mais qualidade e dá uma confiabilidade maior na entrega através de um CI/CD.

A ideia na adesão do Pact não é o de distanciar os times e sim de manter uma comunicação mais sadia entre todos. De forma alguma, por exemplo, um Consumer deve criar um pacto com um determinado Provider sem quaisquer acordo previamente estabelecido, esperando que o Provider implemente aquilo apenas para a sua necessidade momentânea.

Outros dois pontos importantes que valem ser ressaltados é de que a adesão aos testes de contrato devem ser evitados para APIs públicas pois não é possível saber com precisão quantos e quem são seus consumidores e como estes utilizam a sua API. O outro ponto vale reforçar: Testes de contrato devem agregar e não substituir os testes funcionais!

E como próximos passos a ideia é sair dessa parte conceitual e ir adiante na implementação dos testes de contrato em um projeto real. A ideia é mostrar, conforme falado anteriormente, a criação do teste do lado do Consumer, subir o Pact File para o Broker através do Docker (e também via PactFlow) e depois validar junto ao Provider.

Ideias, sugestões e melhorias são sempre bem-vindas. Fique a vontade em comentar e perguntar em caso de dúvidas.

--

--