Image for post
Image for post

Testes de Contrato para Microsserviços com Pact PHP

Fernando Silva
Apr 25, 2018 · 5 min read

O desenvolvimento de sistemas sempre modificou-se rapidamente ao decorrer do tempo. Novos paradigmas de programação foram criados, novos tipos de linguagem, metodologias e processos. A arquitetura dos softwares também não poderia ficar fora, começando com uma arquitetura monolítica, passando para uma arquitetura orientada a serviços (SOA), até a arquitetura com maior destaque no momento, os microsserviços.

O aumento da utilização das arquiteturas baseadas em serviços e microsserviços fez com que um problema entrasse em foco, a quebra de contrato entre o fornecedor do serviço e o cliente.

Um contrato é uma coleção de acordos entre um cliente (Consumer) e uma API (Provider) que descreve as interações que podem ocorrer entre eles.

Consumer Driven Contracts é um padrão que impulsiona o desenvolvimento do provider do ponto de vista do consumer. É o TDD — Test Driven Development (desenvolvimento dirigido por testes) para serviços.

Basicamente o teste funciona da seguinte maneira: o sistema consumidor realiza uma chamada para o provedor e recebe os dados de retorno. A estrutura de dados retornada é comparada com a estrutura definida no contrato. Se os dados não forem iguais o teste falha indicando que a API está retornando dados diferentes do que o cliente está esperando, ou seja, houve a quebra de contrato de algum dado recebido.

A ideia geral é garantir a integridade da API antes que o sistema consumidor processe os dados da requisição enviada pelo provedor, que pode enviar alguma informação inconsistente que o consumidor ainda não consegue identificar como válida.

Testes de contrato com Pact PHP

O Pact, é um framework de teste que ajuda você a escrever contratos, e garante que esses contratos estejam atendidos. O Pact tem implementações para várias linguagens, entre elas, Java, .NET, Javascript, Go, Python, Swift e PHP.

Para melhor entender como o Pact funciona vamos construir um exemplo do zero com PHP. Os exemplos abaixo são baseados na API do Meetup.com. Temos um cenário com um consumer/client e um provider/api.

Primeiro vamos adicionar as dependências que precisamos para realizar os testes, para isso vamos utilizar o composer.

$ composer require phpunit/phpunit — dev$ composer require mattersight/phppact — dev

Ou criando um composer.json com o seguinte conteúdo:

{
"require-dev": {
"phpunit/phpunit": "^6.2",
"mattersight/phppact": "^3.0”
}
}

Vamos criar um diretório src, onde vamos adicionar nossa classe de client com o nome de ‘ExampleOneMeetupApiClient.php’:

Nesta classe estamos fazendo um get para a API de consulta de categorias no sistema do meetup.

Agora criamos um diretório tests e neste diretório um arquivo chamado ‘phpunit.example.one.xml’ para configuração do PHPUnit, com as informações necessárias para executar nossos testes:

E criamos nossa classe de teste no diretório tests que já criamos acima, com o nome de ‘ExampleOneMeetupAPIClientTest.php’ com o seguinte conteúdo:

Nessa classe estamos testando a api de consulta de categorias do meetup. Para isso estamos criando um mock da resposta da api, instanciamos nossa classe de client passando a url do Mock Server. Depois testamos o código de resposta, o formato da resposta e verificamos o conteúdo da resposta, para assim garantir que não temos quebra de contrato.

E por fim executamos nosso teste, entre no diretório tests e execute:

$ php ../vendor/phpunit/phpunit/phpunit -c phpunit.example.one.xml

Temos o seguinte retorno, mostrando os testes que passaram ou não:

PHPUnit 6.5.8 by Sebastian Bergmann and contributors.Starting the mock service with command '/home/fsilva/Documents/dev/php-pact/vendor/mattersight/phppact/src/PhpPact/Standalone/Installer/../../../../pact/bin/pact-mock-service' 'service' '--consumer=ExampleOne' '--provider=ExampleAPI' '--pact-dir=/tmp' '--pact-file-write-mode=overwrite' '--host=localhost' '--port=7200'.
[2018-04-18 22:33:17] INFO WEBrick 1.3.1
[2018-04-18 22:33:17] INFO ruby 2.2.2 (2015-04-13) [x86_64-linux]
[2018-04-18 22:33:17] INFO WEBrick::HTTPServer#start: pid=11881 port=7200
. 1 / 1 (100%)[2018-04-18 22:33:17] INFO going to shutdown ...
[2018-04-18 22:33:18] INFO WEBrick::HTTPServer#start done.
I, [2018-04-18T22:33:17.859136 #11881] INFO -- : Registered expected interaction GET /2/categories
D, [2018-04-18T22:33:17.860161 #11881] DEBUG -- : {
"description": "General Meetup Categories",
"providerState": "A GET request to return JSON using Meetups category api under version 2",
"request": {
"method": "GET",
"path": "/2/categories",
"headers": {
"Content-Type": "application/json"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"results": {
"json_class": "Pact::ArrayLike",
"contents": {
"name": {
"json_class": "Pact::Term",
"data": {
"generate": "Games",
"matcher": {"json_class":"Regexp","o":0,"s":"[gbBG]"}
}
},
"sort_name": "Games",
"id": 11,
"shortname": "Games"
},
"min": 1
}
}
}
}
I, [2018-04-18T22:33:17.870039 #11881] INFO -- : Received request GET /2/categories
D, [2018-04-18T22:33:17.870314 #11881] DEBUG -- : {
"path": "/2/categories",
"query": "",
"method": "get",
"headers": {
"Content-Type": "application/json",
"Host": "localhost:7200",
"User-Agent": "GuzzleHttp/6.3.2 curl/7.55.1 PHP/7.1.16",
"Version": "HTTP/1.1"
}
}
I, [2018-04-18T22:33:17.870853 #11881] INFO -- : Found matching response for GET /2/categories
D, [2018-04-18T22:33:17.871244 #11881] DEBUG -- : {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"results": {
"json_class": "Pact::ArrayLike",
"contents": {
"name": {
"json_class": "Pact::Term",
"data": {
"generate": "Games",
"matcher": {
"json_class": "Regexp",
"o": 0,
"s": "[gbBG]"
}
}
},
"sort_name": "Games",
"id": 11,
"shortname": "Games"
},
"min": 1
}
}
}
I, [2018-04-18T22:33:17.884214 #11881] INFO -- : Verifying - interactions matched
I, [2018-04-18T22:33:17.936178 #11881] INFO -- : Verifying - interactions matched
I, [2018-04-18T22:33:17.941931 #11881] INFO -- : Writing pact for ExampleAPI to /tmp/exampleone-exampleapi.json
Process exited with code 0.
PACT_BROKER_URI environment variable was not set. Skipping PACT file upload.
Time: 2.76 seconds, Memory: 6.00MBOK (1 test, 3 assertions)

Como pode ser visto na resposta dos testes, ao final é gerado um arquivo json com o contrato, como este:

{
"consumer": {
"name": "ExampleOne"
},
"provider": {
"name": "ExampleAPI"
},
"interactions": [
{
"description": "General Meetup Categories",
"providerState": "A GET request to return JSON using Meetups category api under version 2",
"request": {
"method": "GET",
"path": "/2/categories",
"headers": {
"Content-Type": "application/json"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"results": [
{
"name": "Games",
"sort_name": "Games",
"id": 11,
"shortname": "Games"
}
]
},
"matchingRules": {
"$.body.results": {
"min": 1
},
"$.body.results[*].*": {
"match": "type"
},
"$.body.results[*].name": {
"match": "regex",
"regex": "[gbBG]"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "2.0.0"
}
}
}

Neste artigo vimos um exemplo de como pode ser implementado testes de contrato, mais alguns exemplos podem ser encontrado nos seguintes repositórios https://github.com/pact-foundation/pact-php e https://github.com/fernandodebrando/pact-php-example.

Conclusão — Testes de contratos para microsserviços em PHP

A evolução dos sistemas para microsserviços resultou com que novos problemas entrassem em foco, fazendo com que métodos alternativos de testes ganhassem mais destaque além dos testes tradicionais. Apesar de muitas empresas terem experiências ruins na migração para este tipo de arquitetura, existem vantagens bem relevantes em relação a aderir ao uso deste tipo de tecnologia, como builds e entregas mais rápidas e modularidade das API’s. Se quiser saber mais sobre microsserviços, no artigo Microsserviços: Distribuindo serviços críticos ao negócio falo sobre um case que a equipe que eu trabalho na KingHost desenvolveu, abordando vantagens, desvantagens, dificuldades e como migramos nosso sistema monolítico.

E você, qual a sua opinião sobre teste de contrato, já utilizou alguma vez? Deixe um comentário abaixo. Obrigado pela leitura.

Texto originalmente publicado no Blog da KingHost.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store