Contratos orientados ao consumidor ou automação de teste de controle de qualidade com base no Gitlab CI

Os objetivos desta publicação são :


  • Uma Breve Introdução aos Contratos Dirigidos ao Consumidor (CDC)
  • Configurar o pipeline de IC baseado em CDI

Contratos orientados ao consumidor


Nesta parte, abordaremos os principais pontos do CDC. Este artigo não é exaustivo sobre o assunto dos testes de contrato. Há uma quantidade suficiente de materiais sobre esse assunto no mesmo Habré .


Para continuar, precisamos nos familiarizar com as principais disposições do CDC:


  • O teste de contato está no nível Testes de Serviço / Integração acima dos Testes de Unidade, de acordo com a pirâmide de Mike Cohn .
  • O teste de contrato pode ser aplicado quando houver 2 (ou mais) serviços que interagem entre si.
  • A abordagem orientada ao consumidor significa que o primeiro passo na implementação é escrever um teste no lado do consumidor. O resultado do teste é um pacto (contrato) no formato json que descreve a interação entre o consumidor (por exemplo, interface da web / interface móvel: um serviço que deseja receber alguns dados) e o provedor (por exemplo, API do servidor: um serviço que fornece dados)
  • O próximo passo é verificar o contrato com o provedor. Isso é totalmente implementado pela estrutura do Pacto .

Então, vamos começar com o teste do lado do consumidor . Eu usei o Pactman . É assim que o teste se parece:


import pytest from pactman import Like from model.client import Client @pytest.fixture() def consumer(pact): return Client(pact.uri) def test_app(pact, consumer): expected = '123456789' (pact .given('provider in some state') .upon_receiving("request to get user's phone number") .with_request( method='GET', path=f'/phone/john', ) .will_respond_with(200, body=Like(expected)) .given('provider in some state') .upon_receiving("request to get non-existent user's phone number") .with_request( method='GET', path=f'/phone/micky' ) .will_respond_with(404) ) with pact: consumer.get_users_phone(user='john', host=pact.uri) consumer.get_users_phone(user='micky', host=pact.uri) 

Usando o DSL do Pact, descrevemos interações de solicitação / resposta. Depois de iniciar o teste, obtemos um novo arquivo ({consumer} - {provider} -pact.json):


 { "consumer": { "name": 'basic_client' }, "provider": { "name": 'basic_flask_app' }, "interactions": [ { "providerStates": [ { "name": "provider in some state", "params": {} } ], "description": "request to get user's phone number", "request": { "method": "GET", "path": "/phone/john" }, "response": { "status": 200, "body": "123456789", "matchingRules": { "body": { "$": { "matchers": [ { "match": "type" } ] } } } } }, { "providerStates": [ { "name": "provider in some state", "params": {} } ], "description": "request to get non-existent user's phone number", "request": { "method": "GET", "path": "/phone/micky" }, "response": { "status": 404 } } ], "metadata": { "pactSpecification": { "version": "3.0.0" } } } 

Em seguida, precisamos passar o pacto ao provedor para verificação. Isso é feito usando o Pact Broker.


O Pact Broker é um repositório de contratos com alguns recursos adicionais que permitem rastrear a compatibilidade de versões de serviço, além de gerar diagramas de rede (interação de serviço).


Corretor de pacto
imagem


O pacto
imagem


Matriz de versão
imagem


Verificação de fornecedor


Esta parte do teste é realizada completamente pela estrutura. Após a verificação, os resultados são enviados de volta ao Pact Broker.


 provider-verifier_1 | Verifying a pact between basic_client and basic_flask_app provider-verifier_1 | Given provider in some state provider-verifier_1 | request to get user's phone number provider-verifier_1 | with GET /phone/john provider-verifier_1 | returns a response which provider-verifier_1 | WARN: Skipping set up for provider state 'provider in some state' for consumer 'basic_client' as there is no --provider-states-setup-url specified. provider-verifier_1 | has status code 200 provider-verifier_1 | has a matching body provider-verifier_1 | Given provider in some state provider-verifier_1 | request to get non-existent user's phone number provider-verifier_1 | with GET /phone/micky provider-verifier_1 | returns a response which provider-verifier_1 | WARN: Skipping set up for provider state 'provider in some state' for consumer 'basic_client' as there is no --provider-states-setup-url specified. provider-verifier_1 | has status code 404 provider-verifier_1 | provider-verifier_1 | 2 interactions, 0 failures 

Executando as duas partes do teste no pipeline


Agora que as duas partes do teste do contrato foram desmontadas, seria bom executá-las em todas as confirmações. É aqui que o Gitlab CI ajuda. Os trabalhos de pipeline são descritos em .gitlab-ci.yml . Antes de avançarmos para o pipeline, precisamos dizer algumas palavras sobre o GitLab Runner, que é um projeto de código aberto, e é usado para executar tarefas e enviar os resultados de volta ao GitLab. Os trabalhos podem ser executados localmente ou usando contêineres de encaixe. Em nosso projeto, usamos o Docker. A infraestrutura de teste é implementada em contêineres e é descrita em docker-compose.yml , localizado na raiz do projeto.


 version: '2' services: basic-flask-app: image: registry.gitlab.com/tknino69/basic_flask_app:latest ports: - 5005:5005 postgres: image: postgres ports: - 5432:5432 env_file: - test-setup.env volumes: - db-data:/var/lib/postgresql/data/pgdata pactbroker: image: dius/pact-broker links: - postgres ports: - 80:80 env_file: - test-setup.env provider-states: image: registry.gitlab.com/tknino69/cdc/provider-states:latest build: provider-states ports: - 5000:5000 consumer-test: image: registry.gitlab.com/tknino69/cdc/consumer-test:latest command: ["sh", "-c", "find -name '*.pyc' -delete && pytest $${TEST}"] links: - pactbroker environment: - CONSUMER_VERSION=$CI_COMMIT_SHA provider-verifier: image: registry.gitlab.com/tknino69/cdc/provider-verifier:latest build: provider-verifier ports: - 5001:5000 links: - pactbroker depends_on: - consumer-test - provider-states command: ['sh', '-c', 'find -name "*.pyc" -delete && CONSUMER_VERSION=`curl --header "PRIVATE-TOKEN:$${API_TOKEN}" https://gitlab.com/api/v4/projects/$${BASIC_CLIENT}/repository/commits | jq ".[0] .id" | sed -e "s/\x22//g"` && echo $${CONSUMER_VERSION} && pact-provider-verifier $${PACT_BROKER}/pacts/provider/$${PROVIDER}/consumer/$${CONSUMER}/version/$${CONSUMER_VERSION} --provider-base-url=$${BASE_URL} --pact-broker-base-url=$${PACT_BROKER} --provider=$${PROVIDER} --consumer-version-tag=$${CONSUMER_VERSION} --provider-app-version=$${PROVIDER_VERSION} -v --publish-verification-results=PUBLISH_VERIFICATION_RESULTS'] environment: - PROVIDER_VERSION=$CI_COMMIT_SHA - API_TOKEN=$API_TOKEN env_file: - test-setup.env volumes: db-data: 

Portanto, temos serviços que são executados em contêineres conforme necessário.


Fornecedor de Serviços:


 basic-flask-app: image: registry.gitlab.com/tknino69/basic_flask_app:latest ports: - 5005:5005 

Pact Broker e seu banco de dados. Os volumes nos permitem ter um repositório permanente para pactos e resultados da verificação do provedor:


 postgres: image: postgres ports: - 5432:5432 env_file: - test-setup.env volumes: - db-data:/var/lib/postgresql/data/pgdata pactbroker: image: dius/pact-broker links: - postgres ports: - 80:80 env_file: - test-setup.env 

Estados do provedor de serviços. Na prática, ele deve levar o provedor a um determinado estado (por exemplo, coloque o usuário no banco de dados). No entanto, em nosso exemplo, ele simplesmente executa uma função fictícia.


 provider-states: image: registry.gitlab.com/tknino69/cdc/provider-states:latest build: provider-states ports: - 5000:5000 

Um serviço que executa o Teste do Consumidor. Preste atenção ao comando que é executado no contêiner find -name '* .pyc' -delete && pytest $$ {TEST}


 consumer-test: image: registry.gitlab.com/tknino69/cdc/consumer-test:latest command: ["sh", "-c", "find -name '*.pyc' -delete && pytest $${TEST}"] links: - pactbroker environment: - CONSUMER_VERSION=$CI_COMMIT_SHA 

Verificador de provedor de serviços:


 provider-verifier: image: registry.gitlab.com/tknino69/cdc/provider-verifier:latest build: provider-verifier ports: - 5001:5000 links: - pactbroker depends_on: - consumer-test - provider-states command: ['sh', '-c', 'find -name "*.pyc" -delete && CONSUMER_VERSION=`curl --header "PRIVATE-TOKEN:$${API_TOKEN}" https://gitlab.com/api/v4/projects/$${BASIC_CLIENT}/repository/commits | jq ".[0] .id" | sed -e "s/\x22//g"` && echo $${CONSUMER_VERSION} && pact-provider-verifier $${PACT_BROKER}/pacts/provider/$${PROVIDER}/consumer/$${CONSUMER}/version/$${CONSUMER_VERSION} --provider-base-url=$${BASE_URL} --pact-broker-base-url=$${PACT_BROKER} --provider=$${PROVIDER} --consumer-version-tag=$${CONSUMER_VERSION} --provider-app-version=$${PROVIDER_VERSION} -v --publish-verification-results=PUBLISH_VERIFICATION_RESULTS'] environment: - PROVIDER_VERSION=$CI_COMMIT_SHA - API_TOKEN=$API_TOKEN env_file: - test-setup.env 

Pipeline do Consumidor
.gitlab-ci.yml na raiz do projeto do consumidor descreve os processos que são executados no lado do consumidor:


 image: gitlab/dind:latest variables: TEST: 'tests/docker-compose.app.yml' CONSUMER_VERSION: $CI_COMMIT_SHA BASIC_APP: '11993024' services: - gitlab/gitlab-runner:latest before_script: - docker login -u $GIT_USER -p $GIT_PASS registry.gitlab.com stages: - clone_test - get_broker_up - test - verify_provider - clean_up clone test: tags: - cdc stage: clone_test script: - git clone https://$GIT_USER:$GIT_PASS@gitlab.com/tknino69/cdc.git && ls -ali artifacts: paths: - cdc/ broker: tags: - cdc stage: get_broker_up script: - cd cdc && docker-compose -f docker-compose.yml up -d pactbroker dependencies: - clone test test: tags: - cdc stage: test script: - cd cdc && CONSUMER_VERSION=$CONSUMER_VERSION docker-compose -f docker-compose.yml -f $TEST up consumer-test dependencies: - clone test provider verification: tags: - cdc stage: verify_provider script: - curl -X POST -F token=$CI_JOB_TOKEN -F ref=master https://gitlab.com/api/v4/projects/$BASIC_APP/trigger/pipeline when: on_success clean up: tags: - cdc stage: clean_up script: - cd cdc && docker-compose stop consumer-test dependencies: - clone test 

Aqui acontece o seguinte:


No before_script , before_script login no registro do gitlab usando as variáveis ​​$ GIT_USER e $ GIT_PASS que definimos em Configurações> CI / CD
imagem


  • Em seguida, clonamos um projeto de teste
  • Na próxima etapa, elevaremos o Pact Broker
  • Então o Teste do Consumidor é iniciado.
  • Depois disso, use a API do Gitlab para iniciar a verificação do provedor.
  • E, finalmente, nos limpamos

Pipeline do provedor
A configuração do pipeline do provedor é armazenada em .gitlab-ci.yml na raiz do projeto do provedor.


 image: gitlab/dind:latest variables: TEST: 'tests/docker-compose.app.yml' PROVIDER_VERSION: $CI_COMMIT_SHA services: - gitlab/gitlab-runner:latest stages: - clone_test - provider_verification - clean_up clone test: tags: - cdc stage: clone_test script: - git clone https://$GIT_USER:$GIT_PASS@gitlab.com/tknino69/cdc.git artifacts: paths: - cdc/ verify provider: tags: - cdc stage: provider_verification before_script: - cd cdc - docker login -u $GIT_USER -p $GIT_PASS registry.gitlab.com && docker-compose -f docker-compose.yml up -d basic-flask-app script: - PROVIDER_VERSION=$PROVIDER_VERSION docker-compose -f docker-compose.yml -f $TEST up provider-verifier dependencies: - clone test .clean up: tags: - cdc stage: clean_up script: - cd cdc && docker-compose down --rmi local 

Como no pipeline do consumidor, temos vários trabalhos:


  • Clonar um projeto de teste
  • Verificar provedor
  • Nos limpamos

Resuma :


  • Escreveu um teste de contrato em Python
  • Configurar um ambiente de teste em contêineres do Docker
  • IC configurado com base em testes de contrato, ou seja, comprometer-se com o projeto do consumidor iniciará o pipeline do IC ( no lado do consumidor : clonando o ambiente de teste -> iniciando o Pact Broker -> testando o consumidor -> iniciando a verificação do provedor -> limpando; no lado do provedor : clonando o ambiente de teste -> verificação do provedor -> limpando )
    Comprometer-se com o projeto do provedor inicia a verificação do fornecedor para garantir que o fornecedor cumpra o pacto

Obrigado pela atenção.

Source: https://habr.com/ru/post/pt451132/


All Articles