本出版物的目标是 :
- 消费者驱动合同(CDC)简介
- 配置基于CDI的CI管道
消费者驱动的合同
在这一部分中,我们将介绍CDC的要点。 本文在合同测试的主题上并不详尽。 同一哈勃望远镜上有足够的关于这个主题的材料。
要继续,我们需要熟悉CDC的主要规定:
- 根据Mike Cohn 金字塔的观点,联系测试位于单元测试之上的服务/集成测试级别。
- 当有2个(或更多)服务相互交互时,可以应用合同测试。
- 消费者驱动的方法意味着实施的第一步是在消费者侧编写测试。 测试结果是json格式的协议(合同),描述了使用者(例如,Web界面/移动界面:要接收一些数据的服务)与提供者(例如,服务器API:提供数据的服务)之间的交互
- 下一步是与提供者检查合同。 该公约框架已完全实现。
因此,让我们从消费者方面的测试开始 。 我用过Pactman 。 这是测试的样子:
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)
使用Pact DSL,我们描述了请求/响应交互。 开始测试后,我们得到一个新文件({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" } } }
接下来,我们需要将条约传递给提供商进行验证。 这是使用Pact Broker完成的。
Pact Broker是一个合同存储库,具有一些其他功能,这些功能使我们可以跟踪服务版本的兼容性以及生成网络图(服务交互)。
契约经纪人

契约

版本矩阵

提供商验证
测试的这一部分完全由框架执行。 验证后,结果将发送回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
在管道中运行测试的两个部分
既然合同测试的两个部分都已分解,那么在每次提交时都运行它们将是很好的选择。 这就是Gitlab CI进行救援的地方。 管道作业在.gitlab-ci.yml
进行了描述。 在进入管道之前,我们需要对GitLab Runner(这是一个开放源代码项目)说几句话,用于运行作业并将结果发送回GitLab。 作业可以在本地运行,也可以使用docker容器运行。 在我们的项目中,我们使用Docker。 测试基础结构在容器中实现,并在项目根目录的docker-compose.yml
描述。
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:
因此,我们有根据需要在容器中运行的服务。
服务提供者:
basic-flask-app: image: registry.gitlab.com/tknino69/basic_flask_app:latest ports: - 5005:5005
Pact Broker及其数据库。 本书使我们能够拥有一个永久性的资料库,用于提供者验证的协议和结果:
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
运行消费者测试的服务。 注意在容器中运行的命令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
服务提供商验证程序:
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
消费者管道
使用者项目根目录下的.gitlab-ci.yml
描述了在使用者端运行的过程:
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
发生以下情况:
在before_script
我们使用在设置> CI / CD中设置的$ GIT_USER和$ GIT_PASS变量登录到gitlab注册表中

- 接下来,我们克隆一个测试项目
- 下一步,我们筹集Pact Broker
- 然后,消费者测试开始。
- 之后,使用Gitlab API开始提供者验证。
- 最后,我们清理自己
提供者管道
提供程序管道配置存储在提供程序项目根目录中的.gitlab-ci.yml
中。
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
就像在消费者管道中一样,我们有几个工作:
总结一下 :
- 用Python编写合同测试
- 在Docker容器中设置测试环境
- 根据合同测试配置的配置项,即 提交到消费者项目将启动CI管道( 在消费者方面 :克隆测试环境->启动Pact Broker->测试消费者->开始提供者验证->清理; 在提供者方面 :克隆测试环境->提供者验证->清理)
提交到提供者项目将启动提供者验证,以确保提供者遵守协定
谢谢您的关注。