此版本仍在开发中,目前尚不被视为稳定版本。如需最新稳定版本,请使用 spring-cloud-contract 5.0.2spring-doc.cadn.net.cn

Docker 项目

在本节中,我们发布一个包含生成测试并以 EXPLICIT 模式对运行中的应用程序执行测试的项目的 springcloud/spring-cloud-contract Docker 镜像。spring-doc.cadn.net.cn

模式 EXPLICIT 表示从契约生成的测试会发送真实的请求,而不是模拟的请求。

我们还发布了一个 spring-cloud/spring-cloud-contract-stub-runner Docker 镜像,用于启动 Stub Runner 的独立版本。spring-doc.cadn.net.cn

Maven、JAR 文件与二进制存储的简要介绍

由于非 JVM 项目可以使用 Docker 镜像,因此有必要解释 Spring Cloud Contract 打包默认设置背后的基本术语。spring-doc.cadn.net.cn

以下定义的部分内容摘自Maven 术语表spring-doc.cadn.net.cn

  • Project: Maven 以项目为单位进行思考。所有构建的内容都是项目。这些项目遵循一个明确规定的“项目对象模型”(Project Object Model)。项目可以依赖其他项目——在这种情况下,后者被称为“依赖项”。一个项目可能由多个子项目组成。然而,这些子项目仍然被同等视为项目。spring-doc.cadn.net.cn

  • Artifact: 一个构件是指由项目生成或使用的任何东西。Maven 为项目生成的构件示例包括 JAR 文件以及源代码和二进制分发包。每个构件都通过一个组 ID 和一个在该组内唯一的构件 ID 进行唯一标识。spring-doc.cadn.net.cn

  • JAR: JAR 代表 Java 归档文件(Java ARchive)。其格式基于 ZIP 文件格式。Spring Cloud Contract 将契约和生成的存根打包到 JAR 文件中。spring-doc.cadn.net.cn

  • GroupId: 组 ID 是用于标识项目的全局唯一标识符。
    虽然这通常只是项目名称(例如,commons-collections),但建议使用完全限定的包名,以便将其与其他名称相似的项目区分开来(例如,org.apache.maven)。
    通常,当发布到构件管理器时,GroupId 会以斜杠分隔,并成为 URL 的一部分。例如,对于组 ID 为 com.example、构件 ID 为 application 的情况,结果将是 /com/example/application/spring-doc.cadn.net.cn

  • Classifier: Maven 依赖项的表示方式如下:
    groupId:artifactId:version:classifier. 分类器是附加到依赖项的一个后缀——例如,stubssources。相同的依赖项(例如,com.example:application)可以生成多个不同的构件,它们之间的差异即由分类器决定。spring-doc.cadn.net.cn

  • Artifact manager: 当您生成二进制文件、源代码或软件包时,希望它们可供他人下载、引用或复用。在 JVM 生态系统中,这些工件通常为 JAR 文件;对于 Ruby,这些工件是 gem;而对于 Docker,则是 Docker 镜像。您可以将这些工件存储在工件管理器中。此类管理器的示例包括 ArtifactoryNexusspring-doc.cadn.net.cn

生成生产者端的测试

图像搜索 /contracts 文件夹下的合同。运行测试的输出结果位于 /spring-cloud-contract/build 文件夹(便于调试使用)。spring-doc.cadn.net.cn

您可以挂载您的合约并传递环境变量。<br/>然后图像将:<br/>spring-doc.cadn.net.cn

环境变量

Docker 镜像需要一些环境变量来指向您的运行应用程序、构件管理器实例等。以下列表描述了这些环境变量:spring-doc.cadn.net.cn

<p>Table 1. Docker 环境变量</p>

姓名spring-doc.cadn.net.cn

描述spring-doc.cadn.net.cn

默认spring-doc.cadn.net.cn

ADDITIONAL_FLAGSspring-doc.cadn.net.cn

(仅 Docker 镜像) 传递给 Gradle 构建的额外标志spring-doc.cadn.net.cn

DEBUGspring-doc.cadn.net.cn

(仅适用于 Docker 镜像) 仅适用于 Docker 镜像 - 为 Gradle 构建启用调试模式spring-doc.cadn.net.cn

falsespring-doc.cadn.net.cn

EXTERNAL_CONTRACTS_ARTIFACT_IDspring-doc.cadn.net.cn

项目中包含契约的构件IDspring-doc.cadn.net.cn

EXTERNAL_CONTRACTS_CLASSIFIERspring-doc.cadn.net.cn

项目的分类与合同spring-doc.cadn.net.cn

EXTERNAL_CONTRACTS_GROUP_IDspring-doc.cadn.net.cn

项目的组 ID(包含契约)spring-doc.cadn.net.cn

com.examplespring-doc.cadn.net.cn

EXTERNAL_CONTRACTS_PATHspring-doc.cadn.net.cn

给定项目的合同路径,位于包含合同的项目内部。默认为斜杠分隔的 EXTERNAL_CONTRACTS_GROUP_ID 连接 /EXTERNAL_CONTRACTS_ARTIFACT_ID. For example, for group id `cat-server-side.dog 和构件 ID fish,对于合同路径将产生 cat/dog/fishspring-doc.cadn.net.cn

EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_PASSWORDspring-doc.cadn.net.cn

(可选) 如果 EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL 需要身份验证,则为密码。默认值为 `REPO_WITH_BINARIES_PASSWORD`;如果该值未设置,则默认为 `password`。spring-doc.cadn.net.cn

EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URLspring-doc.cadn.net.cn

您的构件管理器的URL。它默认为REPO_WITH_BINARIES_URL环境变量的值;如果该变量未设置,则默认为localhost:8081/artifactory/libs-release-localspring-doc.cadn.net.cn

EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_USERNAMEspring-doc.cadn.net.cn

(可选) 如果 EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL 需要身份验证,则为用户名。默认值为 REPO_WITH_BINARIES_USERNAME。如果未设置,则默认为 `admin`。spring-doc.cadn.net.cn

EXTERNAL_CONTRACTS_VERSIONspring-doc.cadn.net.cn

项目版本包含契约。默认值等效于选择最新版本。spring-doc.cadn.net.cn

+spring-doc.cadn.net.cn

EXTERNAL_CONTRACTS_WORK_OFFLINEspring-doc.cadn.net.cn

如果设置为 true,则从容器的 .m2 中检索带有合约的构件。将本地的 .m2 挂载为卷,并在容器的 /root/.m2 路径下可用。spring-doc.cadn.net.cn

falsespring-doc.cadn.net.cn

FAIL_ON_NO_CONTRACTSspring-doc.cadn.net.cn

如果不存在契约,构建是否会失败?spring-doc.cadn.net.cn

falsespring-doc.cadn.net.cn

MESSAGING_TYPEspring-doc.cadn.net.cn

消息类型。可以是 [rabbit] 或 [kafka]。spring-doc.cadn.net.cn

PRODUCER_STUBS_CLASSIFIERspring-doc.cadn.net.cn

用于生成生产者存根的归档分类器spring-doc.cadn.net.cn

stubsspring-doc.cadn.net.cn

PROJECT_GROUPspring-doc.cadn.net.cn

您项目的组IDspring-doc.cadn.net.cn

com.examplespring-doc.cadn.net.cn

PROJECT_NAMEspring-doc.cadn.net.cn

您的项目的构件IDspring-doc.cadn.net.cn

例举spring-doc.cadn.net.cn

PROJECT_VERSIONspring-doc.cadn.net.cn

您项目的版本spring-doc.cadn.net.cn

0.0.1-SNAPSHOTspring-doc.cadn.net.cn

PUBLISH_ARTIFACTSspring-doc.cadn.net.cn

如果设置为 true,则将构件发布到二进制存储。spring-doc.cadn.net.cn

truespring-doc.cadn.net.cn

PUBLISH_ARTIFACTS_OFFLINEspring-doc.cadn.net.cn

如果设置为 true,则将构件发布到本地 m2。spring-doc.cadn.net.cn

falsespring-doc.cadn.net.cn

PUBLISH_STUBS_TO_SCMspring-doc.cadn.net.cn

如果设置为 true,将执行任务以将存根发布到 SCM。spring-doc.cadn.net.cn

falsespring-doc.cadn.net.cn

REPO_ALLOW_INSECURE_PROTOCOLspring-doc.cadn.net.cn

(可选) 如果 <true> 允许通过不安全的 HTTP 将构件发布到构件管理器。spring-doc.cadn.net.cn

falsespring-doc.cadn.net.cn

REPO_WITH_BINARIES_PASSWORDspring-doc.cadn.net.cn

(可选) 当 Artifact Manager 被保护时的密码spring-doc.cadn.net.cn

密码spring-doc.cadn.net.cn

REPO_WITH_BINARIES_URLspring-doc.cadn.net.cn

您的构件管理器的URL(本地运行时默认为Artifactory的默认URL)spring-doc.cadn.net.cn

localhost:8081/artifactory/libs-release-localspring-doc.cadn.net.cn

REPO_WITH_BINARIES_USERNAMEspring-doc.cadn.net.cn

(可选) 当构件管理器受保护时的用户名spring-doc.cadn.net.cn

管理员spring-doc.cadn.net.cn

STANDALONE_PROTOCOLspring-doc.cadn.net.cn

对于独立版本,应添加哪种额外协议?spring-doc.cadn.net.cn

以下环境变量在运行测试时使用:spring-doc.cadn.net.cn

<Table 2. Docker 环境变量 - 运行时读取>

姓名spring-doc.cadn.net.cn

描述spring-doc.cadn.net.cn

默认spring-doc.cadn.net.cn

APPLICATION_BASE_URLspring-doc.cadn.net.cn

运行该应用程序的URL。spring-doc.cadn.net.cn

APPLICATION_PASSWORDspring-doc.cadn.net.cn

可选密码,用于访问应用程序。spring-doc.cadn.net.cn

APPLICATION_USERNAMEspring-doc.cadn.net.cn

可选的用户名,用于访问该应用。spring-doc.cadn.net.cn

MESSAGING_TRIGGER_CONNECT_TIMEOUTspring-doc.cadn.net.cn

连接超时,触发消息。spring-doc.cadn.net.cn

5000spring-doc.cadn.net.cn

MESSAGING_TRIGGER_READ_TIMEOUTspring-doc.cadn.net.cn

超时读取应用程序对触发消息的响应。spring-doc.cadn.net.cn

5000spring-doc.cadn.net.cn

MESSAGING_TYPEspring-doc.cadn.net.cn

消息类型。可以是 [rabbit] 或 [kafka]。spring-doc.cadn.net.cn

MESSAGING_TYPEspring-doc.cadn.net.cn

定义与基于消息的契约交互时的消息类型。spring-doc.cadn.net.cn

SPRING_KAFKA_BOOTSTRAP_SERVERSspring-doc.cadn.net.cn

Kafka 的代理地址信息。spring-doc.cadn.net.cn

SPRING_RABBITMQ_ADDRESSESspring-doc.cadn.net.cn

对于 RabbitMQ - brokers 地址。spring-doc.cadn.net.cn

自定义 gradle 构建

你可以通过在运行容器时将自定义构建文件挂载为卷来提供要在一个容器中运行的自定义gradle.build:spring-doc.cadn.net.cn

$ docker run -v <absolute-path-of-your-custom-file>:/spring-cloud-contract/build.gradle springcloud/spring-cloud-contract:<version>

示例:通过HTTP进行使用

In this section, we explore a simple MVC application. To get started, clone the following git repository and cd to the resulting directory, by running the following commands:spring-doc.cadn.net.cn

$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore

合同在 /contracts 文件夹中可用。spring-doc.cadn.net.cn

因为我们要运行测试,所以可以运行以下命令:spring-doc.cadn.net.cn

$ npm test

但是,出于学习目的,我们将其分为几个部分,如下所示:spring-doc.cadn.net.cn

# Stop docker infra (nodejs, artifactory)
$ ./stop_infra.sh
# Start docker infra (nodejs, artifactory)
$ ./setup_infra.sh

# Kill & Run app
$ pkill -f "node app"
$ nohup node app &

# Prepare environment variables
$ SC_CONTRACT_DOCKER_VERSION="..."
$ APP_IP="192.168.0.100"
$ APP_PORT="3000"
$ ARTIFACTORY_PORT="8081"
$ APPLICATION_BASE_URL="http://${APP_IP}:${APP_PORT}"
$ ARTIFACTORY_URL="http://${APP_IP}:${ARTIFACTORY_PORT}/artifactory/libs-release-local"
$ CURRENT_DIR="$( pwd )"
$ CURRENT_FOLDER_NAME=${PWD##*/}
$ PROJECT_VERSION="0.0.1.RELEASE"

# Run contract tests
$ docker run  --rm -e "APPLICATION_BASE_URL=${APPLICATION_BASE_URL}" -e "PUBLISH_ARTIFACTS=true" -e "PROJECT_NAME=${CURRENT_FOLDER_NAME}" -e "REPO_WITH_BINARIES_URL=${ARTIFACTORY_URL}" -e "PROJECT_VERSION=${PROJECT_VERSION}" -v "${CURRENT_DIR}/contracts/:/contracts:ro" -v "${CURRENT_DIR}/node_modules/spring-cloud-contract/output:/spring-cloud-contract-output/" springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"

# Kill app
$ pkill -f "node app"

通过 bash 脚本,发生以下情况:spring-doc.cadn.net.cn

消息驱动式使用示例

如果您希望使用消息传递的Docker镜像(例如在多语言应用程序的情况下)与Spring Cloud Contract一起使用,则必须满足以下前提条件:spring-doc.cadn.net.cn

  • 中间件(例如 RabbitMQ 或 Kafka)必须在生成测试之前运行spring-doc.cadn.net.cn

  • 你的合同需要调用一个方法 triggerMessage(…​),并传入一个等于合同的 labelString 参数。spring-doc.cadn.net.cn

  • 你的应用程序需要具有一个HTTP端点,通过该端点我们可以触发消息spring-doc.cadn.net.cn

消息契约示例

The contract needs to call a triggerMessage(…​) method. That method is already provided in the base class for all tests in the docker image and will send out a request to the HTTP endpoint on the producer side. Below you can find examples of such contracts.spring-doc.cadn.net.cn

Groovy
import org.springframework.cloud.contract.spec.Contract

Contract.make {
    description 'Send a pong message in response to a ping message'
    label 'ping_pong'
    input {
        // You have to provide the `triggerMessage` method with the `label`
        // as a String parameter of the method
        triggeredBy('triggerMessage("ping_pong")')
    }
    outputMessage {
        sentTo('output')
        body([
            message: 'pong'
        ])
    }
    metadata(
        [amqp:
         [
           outputMessage: [
               connectToBroker: [
                   declareQueueWithName: "queue"
               ],
                messageProperties: [
                    receivedRoutingKey: '#'
                ]
           ]
         ]
        ])
}
YAML
description: 'Send a pong message in response to a ping message'
label: 'ping_pong'
input:
    # You have to provide the `triggerMessage` method with the `label`
    # as a String parameter of the method
    triggeredBy: 'triggerMessage("ping_pong")'
outputMessage:
    sentTo: 'output'
    body:
        message: 'pong'
metadata:
    amqp:
        outputMessage:
            connectToBroker:
                declareQueueWithName: "queue"
            messageProperties:
                receivedRoutingKey: '#'

HTTP端点可触发消息

为什么需要开发这样的端点?Spring Cloud Contract 需要在各种语言(如 Java)中生成代码,以便能够触发向代理发送消息的生产代码。如果没有生成这些代码,那么就必须提供一个 HTTP 端点,用户可以使用自己选择的语言进行准备。spring-doc.cadn.net.cn

The endpoint must have the following configuration:spring-doc.cadn.net.cn

下面是一个这样的端点的示例。如果你有兴趣在你的语言中提供一个示例,请不要犹豫,在中创建一个问题。spring-doc.cadn.net.cn

Python
#!/usr/bin/env python

from flask import Flask
from flask import jsonify
import pika
import os

app = Flask(__name__)

# Production code that sends a message to RabbitMQ
def send_message(cmd):
    connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
    channel = connection.channel()
    channel.basic_publish(
        exchange='output',
        routing_key='#',
        body=cmd,
        properties=pika.BasicProperties(
            delivery_mode=2,  # make message persistent
        ))
    connection.close()
    return " [x] Sent via Rabbit: %s" % cmd

# This should be ran in tests (shouldn't be publicly available)
if 'CONTRACT_TEST' in os.environ:
    @app.route('/springcloudcontract/<label>', methods=['POST'])
    def springcloudcontract(label):
        if label == "ping_pong":
            return send_message('{"message":"pong"}')
        else:
            raise ValueError('No such label expected.')

(正在生产者端运行消息测试)

现在,让我们从合约生成测试用例来测试生产者端。我们将运行 bash 脚本以启动带有所附合约的 Docker 映像,但是我们还将添加变量使消息传递代码正常工作。在这种情况下,假设合约被存储在一个 Git 仓库中。spring-doc.cadn.net.cn

#!/bin/bash
set -x

CURRENT_DIR="$( pwd )"

export SC_CONTRACT_DOCKER_VERSION="${SC_CONTRACT_DOCKER_VERSION:-4.0.1-SNAPSHOT}"
export APP_IP="$( ./whats_my_ip.sh )"
export APP_PORT="${APP_PORT:-8000}"
export APPLICATION_BASE_URL="http://${APP_IP}:${APP_PORT}"
export PROJECT_GROUP="${PROJECT_GROUP:-group}"
export PROJECT_NAME="${PROJECT_NAME:-application}"
export PROJECT_VERSION="${PROJECT_VERSION:-0.0.1-SNAPSHOT}"
export PRODUCER_STUBS_CLASSIFIER="${PRODUCER_STUBS_CLASSIFIER:-stubs}"
export FAIL_ON_NO_CONTRACTS="${FAIL_ON_NO_CONTRACTS:-false}"
# In our Python app we want to enable the HTTP endpoint
export CONTRACT_TEST="true"
# In the Verifier docker container we want to add support for RabbitMQ
export MESSAGING_TYPE="rabbit"

# Let's start the infrastructure (e.g. via Docker Compose)
yes | docker-compose kill || echo "Nothing running"
docker-compose up -d

echo "SC Contract Version [${SC_CONTRACT_DOCKER_VERSION}]"
echo "Application URL [${APPLICATION_BASE_URL}]"
echo "Project Version [${PROJECT_VERSION}]"

# Let's run python app
gunicorn -w 4 --bind 0.0.0.0 main:app &
APP_PID=$!

# Generate and run tests
docker run  --rm \
                --name verifier \
                # For the image to find the RabbitMQ running in another container
                -e "SPRING_RABBITMQ_ADDRESSES=${APP_IP}:5672" \
                # We need to tell the container what messaging middleware we will use
                -e "MESSAGING_TYPE=${MESSAGING_TYPE}" \
                -e "PUBLISH_STUBS_TO_SCM=false" \
                -e "PUBLISH_ARTIFACTS=false" \
                -e "APPLICATION_BASE_URL=${APPLICATION_BASE_URL}" \
                -e "PROJECT_NAME=${PROJECT_NAME}" \
                -e "PROJECT_GROUP=${PROJECT_GROUP}" \
                -e "PROJECT_VERSION=${PROJECT_VERSION}" \
                -e "EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL=git://https://github.com/marcingrzejszczak/cdct_python_contracts.git" \
                -e "EXTERNAL_CONTRACTS_ARTIFACT_ID=${PROJECT_NAME}" \
                -e "EXTERNAL_CONTRACTS_GROUP_ID=${PROJECT_GROUP}" \
                -e "EXTERNAL_CONTRACTS_VERSION=${PROJECT_VERSION}" \
                -v "${CURRENT_DIR}/build/spring-cloud-contract/output:/spring-cloud-contract-output/" \
                springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"

kill $APP_PID

yes | docker-compose kill

会发生什么:spring-doc.cadn.net.cn

  • 测试用例将从Git获取的契约中生成spring-doc.cadn.net.cn

  • 我们在合同中提供了一个元数据条目,名为declareQueueWithName,这将在RabbitMQ中创建一个带有给定名称的队列在发送触发消息的请求之前spring-doc.cadn.net.cn

  • 通过对triggerMessage("ping_pong")方法的调用,将向Python应用的/springcloudcontract/ping_pong端点发送POST请求spring-doc.cadn.net.cn

  • Python 应用程序将通过 RabbitMQ 发送 0 的 JSON 到名为 1 的交换机spring-doc.cadn.net.cn

  • 生成的测试将轮询发送到output交换机的消息spring-doc.cadn.net.cn

  • 一旦收到消息,就会断言其内容spring-doc.cadn.net.cn

测试通过后我们知道消息已从Python应用程序正确发送到RabbitMQ。spring-doc.cadn.net.cn

在使用者端运行存档文件

本节介绍如何在使用者端使用Docker来获取并运行存档。spring-doc.cadn.net.cn

我们发布了一个spring-cloud/spring-cloud-contract-stub-runner Docker 镜像,它启动了 Stub Runner 的独立版本。spring-doc.cadn.net.cn

安全

由于Spring Cloud Contract Stub Runner Docker Image使用的是Stub Runner的独立版本,需要采取相同的安保措施。 有关这些考虑因素的更多信息,请参阅文档中的 此部分 spring-doc.cadn.net.cn

环境变量

你可以运行Docker镜像并将任何的 JUnit和Spring的通用属性 作为环境变量传递。约定是所有字母都应使用大写。 点(.)应替换为下划线(_)字符。例如, stubrunner.repositoryRoot 属性应表示为 STUBRUNNER_REPOSITORY_ROOT 环境变量。spring-doc.cadn.net.cn

除以上变量外,还可以设置以下变量:spring-doc.cadn.net.cn

  • MESSAGING_TYPE - 在使用哪种消息传递系统(目前支持的有rabbitkafkaspring-doc.cadn.net.cn

  • ADDITIONAL_OPTS - 可选传递给应用层的其他属性,这些属性不会被Spring框架处理。spring-doc.cadn.net.cn

示例用法

我们希望使用在本 [docker-server-side] 步骤中创建的存根。 假设我们希望在端口 9876 上运行存根。你可以通过克隆仓库并切换到以下命令中指示的目录查看 NodeJS 代码。spring-doc.cadn.net.cn

$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore

现在我们可以通过运行以下命令来运行 Stub Runner Boot 应用程序,并使用存档,如下面所示:spring-doc.cadn.net.cn

# Provide the Spring Cloud Contract Docker version
$ SC_CONTRACT_DOCKER_VERSION="..."
# The IP at which the app is running and Docker container can reach it
$ APP_IP="192.168.0.100"
# Spring Cloud Contract Stub Runner properties
$ STUBRUNNER_PORT="8083"
# Stub coordinates 'groupId:artifactId:version:classifier:port'
$ STUBRUNNER_IDS="com.example:bookstore:0.0.1.RELEASE:stubs:9876"
$ STUBRUNNER_REPOSITORY_ROOT="http://${APP_IP}:8081/artifactory/libs-release-local"
# Run the docker with Stub Runner Boot
$ docker run  --rm \
    -e "STUBRUNNER_IDS=${STUBRUNNER_IDS}" \
    -e "STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}" \
    -e "STUBRUNNER_STUBS_MODE=REMOTE" \
    -p "${STUBRUNNER_PORT}:${STUBRUNNER_PORT}" \
    -p "9876:9876" \
    springcloud/spring-cloud-contract-stub-runner:"${SC_CONTRACT_DOCKER_VERSION}"

当先前的命令运行时,spring-doc.cadn.net.cn

在服务器端,我们构建了一个有状态存根。我们可以使用curl来断言存根设置是否正确。为此,运行以下命令:spring-doc.cadn.net.cn

# let's run the first request (no response is returned)
$ curl -H "Content-Type:application/json" -X POST --data '{ "title" : "Title", "genre" : "Genre", "description" : "Description", "author" : "Author", "publisher" : "Publisher", "pages" : 100, "image_url" : "https://d213dhlpdb53mu.cloudfront.net/assets/pivotal-square-logo-41418bd391196c3022f3cd9f3959b3f6d7764c47873d858583384e759c7db435.svg", "buy_url" : "https://pivotal.io" }' http://localhost:9876/api/books
# Now time for the second request
$ curl -X GET http://localhost:9876/api/books
# You will receive contents of the JSON
如果您要在主机上使用您自己构建的存档,
应设置-e STUBRUNNER_STUBS_MODE=LOCAL环境变量并装载本地m2的卷(-v "${HOME}/.m2/:/home/scc/.m2:rw")。

使用消息传递的示例

要使消息传递正常工作,只需传递环境变量MESSAGING_TYPE,其值为kafkarabbit。这将导致设置Stub Runner Boot Docker映像,该映像包含连接到代理所需的所有依赖项。spring-doc.cadn.net.cn

为了设置连接属性,您可以查看Spring Cloud Stream属性页面以设置正确的环境变量。spring-doc.cadn.net.cn

您要设置的最常见属性是运行中间件的位置。
如果有一个属性来设置它,名为spring.rabbitmq.addressesspring.kafka.bootstrap-servers,那么您应该分别将环境变量命名为SPRING_RABBITMQ_ADDRESSESSPRING_KAFKA_BOOTSTRAP_SERVERS
spring-doc.cadn.net.cn

运行现有中间件的合同测试

合法的理由可以运行合同测试,以现有的中间件。一些测试框架可能会给你错误地返回真结果 - 测试通过构建中,而在生产环境中,通信失败。spring-doc.cadn.net.cn

在Spring Cloud Contract Docker镜像中,我们提供了一个连接到现有中间件的选项。如前所述,我们开箱即用地支持Kafka和RabbitMQ。但是,通过Apache Camel组件,我们也可以支持其他中间件。让我们来看看以下用法示例。spring-doc.cadn.net.cn

Spring Cloud Contract Docker 和运行中间件

为了连接到任意的中间件,我们将利用合同部分中的standalone元数据条目spring-doc.cadn.net.cn

description: 'Send a pong message in response to a ping message'
label: 'standalone_ping_pong' (1)
input:
  triggeredBy: 'triggerMessage("ping_pong")' (2)
outputMessage:
  sentTo: 'rabbitmq:output' (3)
  body: (4)
    message: 'pong'
metadata:
  standalone: (5)
    setup: (6)
      options: rabbitmq:output?queue=output&routingKey=(7)
    outputMessage: (8)
      additionalOptions: routingKey=#&queue=output (9)
1 使用此标签触发存档库消息
2 正如在之前的 messaging 例子中一样,我们需要在运行中的应用程序中触发 HTTP 端点,使其按照提供的协议发送消息
3 protocol:destination 如 Apache Camel 所请求的
4 输出消息正文
5 独立元数据条目
6 设置部分将包含有关在实际调用正在运行的应用程序的HTTP端点之前如何准备进行合同测试的信息
7 要调用设置阶段的 Apache Camel URI。在这种情况下,我们将尝试在以下位置轮询消息:output交换和导致拥有queue=outputroutingKey=一个带有名称的队列outputwill be set and bound to theoutput与路由键交换
8 额外选项(更技术性的选项)将追加到第(3)点中的protocol:destination,将一起组合成以下格式rabbitmq:output?routingKey=#&queue=output

对于合同测试通过,我们将像在多语言环境的消息传递情况下一样通常需要运行的应用程序和运行中间件。这次我们将在Spring Cloud Contract Docker映像上设置不同的环境变量。spring-doc.cadn.net.cn

#!/bin/bash
set -x

# Setup
# Run the middleware
docker-compose up -d rabbitmq (1)

# Run the python application
gunicorn -w 4 --bind 0.0.0.0 main:app & (2)
APP_PID=$!

docker run  --rm \
                --name verifier \
                -e "STANDALONE_PROTOCOL=rabbitmq" \ (3)
                -e "CAMEL_COMPONENT_RABBITMQ_ADDRESSES=172.18.0.1:5672" \ (4)
                -e "PUBLISH_STUBS_TO_SCM=false" \
                -e "PUBLISH_ARTIFACTS=false" \
                -e "APPLICATION_BASE_URL=172.18.0.1" \
                -e "PROJECT_NAME=application" \
                -e "PROJECT_GROUP=group" \
                -e "EXTERNAL_CONTRACTS_ARTIFACT_ID=application" \
                -e "EXTERNAL_CONTRACTS_GROUP_ID=group" \
                -e "EXTERNAL_CONTRACTS_VERSION=0.0.1-SNAPSHOT" \
                -v "${CURRENT_DIR}/build/spring-cloud-contract/output:/spring-cloud-contract-output/" \
                springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"


# Teardown
kill $APP_PID
yes | docker-compose kill
1 需要先运行中间件
2 该应用程序需要正常运行
3 通过STANDALONE_PROTOCOL环境变量,我们将获取一个Apache Camel组件。要获取的工件是org.apache.camel.springboot:camel-${STANDALONE_PROTOCOL}-starter。换句话说,STANDALONE_PROTOCOL与Camel的组件匹配。
4 我们正在通过Camel的Spring Boot Starter机制设置地址(我们可能是在设置凭据)。 例如:Apache Camel的RabbitMQ Spring Boot自动配置

Stub Runner Docker 和运行中间件

为了在正在运行的中间件上触发一个占位消息,我们可以按照以下方式运行 Stub Runner Docker 镜像。spring-doc.cadn.net.cn

$ docker run \
    -e "CAMEL_COMPONENT_RABBITMQ_ADDRESSES=172.18.0.1:5672" \ (1)
    -e "STUBRUNNER_IDS=group:application:0.0.1-SNAPSHOT" \ (2)
    -e "STUBRUNNER_REPOSITORY_ROOT=git://https://github.com/marcingrzejszczak/cdct_python_contracts.git" \ (3)
    -e ADDITIONAL_OPTS="--thin.properties.dependencies.rabbitmq=org.apache.camel.springboot:camel-rabbitmq-starter:3.4.0" \ (4)
    -e "STUBRUNNER_STUBS_MODE=REMOTE" \ (5)
    -v "${HOME}/.m2/:/home/scc/.m2:rw" \ (6)
    -p 8750:8750 \ (7)
    springcloud/spring-cloud-contract-stub-runner:3.0.4-SNAPSHOT (8)
1 我们正在通过 Apache Camel 的 Spring Boot 自动配置 将 RabbitMQ 地址注入。
2 我们告诉Stub Runner下载哪些存档
3 我们为 stubs 提供一个外部位置(Git 仓库)
4 通过ADDITIONAL_OPTS=--thin.properties.dependencies.XXX=GROUP:ARTIFACT:VERSION属性,我们告诉Stub Runner在运行时要获取哪个额外的依赖。在这种情况下,我们想获取camel-rabbitmq-starter,所以XXX是随机字符串,我们要获取org.apache.camel.springboot:camel-rabbitmq-starter工件在版本3.4.0
5 由于我们正在使用Git,所以需要设置提取存根的远程选项
6 因为要加快 Stub Runner 的启动速度,我们将本地 Maven 仓库 .m2 作为卷附加。如果您没有填充它,可以考虑使用 :rw 进行读写权限,或使用 :ro 进行只读权限。
7 我们公开了Stub Runner正在运行的端口8750
8 坐标轴Stub RunnerDocker映像。

After a while you’ll notice the following text in your console, which means that Stub Runner is ready to accept requests.spring-doc.cadn.net.cn

o.a.c.impl.engine.AbstractCamelContext   : Apache Camel 3.4.3 (camel-1) started in 0.007 seconds
o.s.c.c.s.server.StubRunnerBoot          : Started StubRunnerBoot in 14.483 seconds (JVM running for 18.666)
o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
o.s.web.servlet.DispatcherServlet        : Completed initialization in 2 ms

要获取触发器列表,您可以向localhost:8750/triggers端点发送HTTP GET请求。若要触发存档消息,可向localhost:8750/triggers/standalone_ping_pong端点发送HTTP POST请求。在控制台中,您会看到:spring-doc.cadn.net.cn

o.s.c.c.v.m.camel.CamelStubMessages      : Will send a message to URI [rabbitmq:output?routingKey=#&queue=output]

如果你检查RabbitMQ管理控制台,你会看到在output队列中可用有1条消息。spring-doc.cadn.net.cn