Atualmente, o desenvolvimento de sistemas deixou de ser limitado a apenas um grande sistema monolito que faz todas as funcionalidades.
Estamos constantemente evoluindo nossos softwares para que tenhamos diversos sistemas que comunicam-se entre si e essa comunicação pode ser feita por algum protocolo de rede, por exemplo: HTTP, AMQP, GRPC, entre outros protocolos que funcionam sob o TCP/IP.
Com isso, surge uma grande necessidade no fluxo de desenvolvimento de software que são os testes integrados. Beleza, mas o que são os testes integrados e o que eles resolvem? Como o PACT pode nos ajudar a testar ainda mais nossas integrações? Bora descobrir!
Expondo serviços
Atualmente, com os novos “frameworks de caixinha”, ficou fácil para os desenvolvedores proverem serviços.
Prover um serviço, ou expor um serviço é, na verdade, expor um comportamento sistêmico por um meio de comunicação de rede, similar ao mencionado no parágrafo acima.
Na maioria das vezes essa comunicação é feita por REST (HTTP). No entanto, quando dizemos que alguma equipe de desenvolvedores expôs um serviço, nada mais é do que um sistema que expôs uma parte de seu comportamento para outros sistemas o consumirem, e quando esses o consumirem, é o que chamamos de integração, uma aplicação que necessita da ação de outra aplicação.
Em qualquer integração é necessário:
- Um protocolo de comunicação
- Uma assinatura entre quem provê e quem consome (Entrada e saída de dados)
- Autenticação e autorização (Dependendo da regra de negócio)
Veja um exemplo de vários serviços fazendo integração via REST.
Na imagem acima, vemos um cenário clássico de microserviços REST. O fluxo começa de um browser, tanto um smartphone ou um desktop que solicita alguma ação sistêmica, e “por baixo dos panos” essa regra de negócio é executada em diversos microserviços via comunicação REST.
Neste caso, toda vez que há uma comunicação REST existe um contrato entre as pontas, onde o PROVIDER (quem provê um comportamento) recebe na requisição e o CONSUMER (consumidor de algum comportamento) que precisa enviar a informação conforme o provider necessita para que haja uma interação, e é essa interação que chamamos de integração.
Na imagem abaixo, um exemplo mais detalhado de uma comunicação entre consumer e provider onde o cliente envia algumas informações para obter a previsão do tempo.
O que são Testes Integrados?
Os testes integrados, normalmente, são realizados após os testes unitários, em resumo, como o nome já diz, testes integrados são testes que validam a funcionalidade de um ou mais sistemas e também a comunicação entre eles para garantir determinado comportamento.
Na maioria das vezes é criado um código em forma de teste que faz uma chamada real para um serviço em ambiente integrado (que abordaremos em breve), nisso, validamos a requisição e a resposta da integração, realizamos asserções no qual garantimos o comportamento esperado entre sistema A e B.
Na imagem abaixo, podemos ver um exemplo de teste integrado entre duas APIs REST. O teste sempre é escrito e executado na API que está consumindo o serviço para criar a chamada e realizar as asserções.
Ambiente integrado
Quando estamos desenvolvendo um sistema, costumamos ter pelo menos três ambientes: o de desenvolvimento, o integrado e o produtivo.
O de desenvolvimento é a própria máquina do desenvolvedor, onde ele constrói e acaba fazendo testes simples conforme está desenvolvendo.
O ambiente produtivo é o ambiente real onde os clientes têm acesso a plataforma. Espera-se que nesse ambiente tudo já tenha sido testado e todas as integrações estejam testadas e funcionando.
E por fim o ambiente de integração, que é muito utilizado pelos desenvolvedores de software e pela equipe de Q.A (Quality Assurance).
O ambiente integrado é um ambiente controlado, visível apenas pela própria empresa que está desenvolvendo a aplicação para garantir que tudo esteja funcionando antes de ir para o ambiente produtivo. Aqui é onde rola todos os testes funcionais simulando a interação de clientes na plataforma para termos certeza que tudo está conforme esperamos.
Se estamos falando que o ambiente integrado simula o ambiente produtivo, aqui surgem alguns desafios:
- Criar toda essa infraestrutura “similar” ao ambiente produtivo, com todos serviços necessários (sistemas, bancos de dados, serviços intermediários).
- Gerar massa de dados correspondentes à regra de negócio para que os sistemas consigam simular os cenários de integração: ex: um carrinho de compras.
- Garantir que essas massas de dados não sejam compartilhadas por outras equipes ou sistemas para não corrompermos a integridade dos dados e nisso recebermos falso negativo nos testes.
Desafios de um ambiente integrado
Mesmo que consigamos ter um ambiente integrado bem estruturado, ainda existem alguns desafios que foram pouco desbravados pelo mundo de arquitetura de software, como um exemplo:
- Como o provider (que irá expor um comportamento) poderá atualizar sua integração (nome da URL, dados de resposta) e garantir que todos que estão se comunicando com ele não irão “quebrar”, ou seja, como ter certeza que podemos publicar uma nova versão e que as outras pontas que consomem atualizaram seu lado também? Veja imagem abaixo como exemplo.
No exemplo acima, testes integrados no geral não validam a mudança da assinatura da integração de forma automatizada, cada empresa resolve do seu jeito, na maioria das vezes, executam todos os testes integrados (que costumam ser pesados) para garantir as integrações toda vez que as aplicações são alteradas.
Diante desse cenário, os desenvolvedores de software costumam versionar suas APIs para contornar algumas possíveis falhas de integração, mas, mesmo com ela, ainda existem muitos desafios para serem resolvidos.
Vamos descrever um cenário inusitado que essa implementação não irá resolver:
→ Desenvolvemos uma API Rest (versionamos a API) e duas outras equipes de desenvolvimento estão consumindo nossas APIs.
→ Fizemos todos os testes integrados com muito sacrifício, nosso sistema está no ar em produção e está tudo lindo e maravilhoso! Certo dia, sofremos um ataque hacker no sistema e descobrimos uma vulnerabilidade exposta na integração, bem na API que desenvolvemos. Agora vamos arrumar as falhas de segurança, mas nesse fix, tivemos que alterar a assinatura da integração, isso impactará as outras duas equipes que estão consumindo essa API.
→ Bom, acho que não teremos problemas, iremos publicar o fix numa nova versão da API, publicaremos e comunicaremos esses dois times para que quando possível desenvolvam suas integrações para a nova versão da nossa API. Depois disso desativaremos a API insegura, tudo bem, certo? Na verdade NÃO! Pois não podemos deixar uma API legada vulnerável exposta.
Cenários como esse descrito acima, podem ser exceção, mas você nunca saberá quando ele irá acontecer.
O ponto que quero chegar é que ainda há muito para se explorar de como criamos nossas integrações e como garantimos que os dois lados (provedor e consumidor) estejam OK antes de subirmos essa integração para o ambiente produtivo. Também temos desafios quando o assunto é prover ambiente integrado para testes, como toda sua infraestrutura, por exemplo.
Esses desafios ainda são resolvidos de muitas maneiras diferentes em ambientes de desenvolvimento, e pode ter certeza que existem muitos problemas não resolvidos nesses desafios.
Resumindo o PACT
O PACT é um framework de teste de contratos que implementa o conceito de Consumer Driven Contract. Com ele mudamos um pouco a maneira que garantimos o funcionamento de nossas integrações eliminando a necessidade do ambiente integrado, pois as integrações validam apenas seus contratos de comunicação com a abordagem do PACT.
Abordaremos melhor toda sua estrutura no próximo artigo, parte 2. Se inscreva na nossa newsletter para receber a série completa em primeira mão.
Leia aqui o projeto completo.
E você, como faz testes integrados em seus sistemas? Conta pra gente nos comentários!
No próximo tópico iremos explicar os conceitos sobre Consumer Driven Contract e também sobre o PACT. Temos um caminho grande para desbravar, bora?