基于外部仓库契约的消费者驱动契约

在这个流程中,我们执行消费者驱动契约测试。契约定义存储在一个独立的仓库中。

先决条件

要使用基于外部仓库契约的消费者驱动契约,你需要设置一个 Git 仓库,该仓库需要:

  • 包含每个生产者的所有契约定义。

  • 能够将契约定义打包成 JAR。

  • 对于每个契约生产者,包含一种方式(例如 pom.xml)通过 Spring Cloud Contract 插件 (SCC Plugin) 在本地安装存根。

更多信息请参阅“如何做”部分,其中描述了如何设置这样一个仓库。此类项目的示例请参阅这个示例

你还需要设置了 Spring Cloud Contract Stub Runner 的消费者代码。此类项目的示例请参阅这个示例。你还需要设置了 Spring Cloud Contract 和其插件的生产者代码。此类项目的示例请参阅这个示例。存根存储可以是 Nexus 或 Artifactory。

概括而言,流程如下:

  1. 消费者使用来自独立仓库的契约定义进行工作。

  2. 消费者工作完成后,在消费者侧创建一个包含工作代码的分支,并向保存契约定义的独立仓库提交一个 pull request。

  3. 生产者接管向保存契约定义的独立仓库提交的 pull request,并在本地安装包含所有契约的 JAR。

  4. 生产者从本地存储的 JAR 生成测试,并编写缺失的实现代码以使测试通过。

  5. 生产者工作完成后,合并向保存契约定义的仓库提交的 pull request。

  6. CI 工具构建保存契约定义的仓库并将包含契约定义的 JAR 上传到 Nexus 或 Artifactory 后,生产者可以合并其分支。

  7. 最后,消费者可以切换到在线工作模式,从远程位置获取生产者的存根,并将该分支合并到主分支。

消费者流程

消费者:

  1. 编写一个向生产者发送请求的测试。

    由于没有服务器存在,测试失败。

  2. 克隆保存契约定义的仓库。

  3. 在文件夹下设置需求作为契约,以消费者名称作为生产者的子文件夹。

    例如,对于名为 producer 的生产者和名为 consumer 的消费者,契约将存储在 src/main/resources/contracts/producer/consumer/ 目录下。

  4. 定义契约后,将生产者存根安装到本地存储,如下例所示:

    $ cd src/main/resource/contracts/producer
    $ ./mvnw clean install
  5. 在消费者测试中设置 Spring Cloud Contract (SCC) Stub Runner,以便:

    • 从本地存储获取生产者存根。

    • 在 stubs-per-consumer 模式下工作(这启用消费者驱动契约模式)。

      SCC Stub Runner 会:

    • 获取生产者存根。

    • 运行一个包含生产者存根的内存中 HTTP 服务器存根。现在你的测试与 HTTP 服务器存根通信,并且你的测试通过。

    • 向保存契约定义的仓库创建一个包含生产者新契约的 pull request。

    • 分支你的消费者代码,直到生产者团队合并他们的代码。

以下 UML 图显示了消费者流程:

flow-overview-consumer-cdc-external-consumer

生产者流程

生产者:

  1. 接管向保存契约定义的仓库提交的 pull request。你可以从命令行执行此操作,如下所示:

    $ git checkout -b the_branch_with_pull_request master
    git pull https://github.com/user_id/project_name.git the_branch_with_pull_request
  2. 安装契约定义,如下所示:

    $ ./mvnw clean install
  3. 设置插件从 JAR 而非 src/test/resources/contracts 中获取契约定义,如下所示:

    Maven
    <plugin>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
    	<version>${spring-cloud-contract.version}</version>
    	<extensions>true</extensions>
    	<configuration>
    		<!-- We want to use the JAR with contracts with the following coordinates -->
    		<contractDependency>
    			<groupId>com.example</groupId>
    			<artifactId>beer-contracts</artifactId>
    		</contractDependency>
    		<!-- The JAR with contracts should be taken from Maven local -->
    		<contractsMode>LOCAL</contractsMode>
    		<!-- ... additional configuration -->
    	</configuration>
    </plugin>
    Gradle
    contracts {
    	// We want to use the JAR with contracts with the following coordinates
    	// group id `com.example`, artifact id `beer-contracts`, LATEST version and NO classifier
    	contractDependency {
    		stringNotation = 'com.example:beer-contracts:+:'
    	}
    	// The JAR with contracts should be taken from Maven local
    	contractsMode = "LOCAL"
    	// Additional configuration
    }
  4. 运行构建以生成测试和存根,如下所示:

    Maven
    ./mvnw clean install
    Gradle
    ./gradlew clean build
  5. 编写缺失的实现代码,以使测试通过。

  6. 合并向保存契约定义的仓库提交的 pull request,如下所示:

    $ git commit -am "Finished the implementation to make the contract tests pass"
    $ git checkout master
    $ git merge --no-ff the_branch_with_pull_request
    $ git push origin master

    CI 系统构建包含契约定义的项目,并将包含契约定义的 JAR 上传到 Nexus 或 Artifactory。

  7. 切换到远程工作模式。

  8. 设置插件以便契约定义不再从本地存储获取,而是从远程位置获取,如下所示:

    Maven
    <plugin>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
    	<version>${spring-cloud-contract.version}</version>
    	<extensions>true</extensions>
    	<configuration>
    		<!-- We want to use the JAR with contracts with the following coordinates -->
    		<contractDependency>
    			<groupId>com.example</groupId>
    			<artifactId>beer-contracts</artifactId>
    		</contractDependency>
    		<!-- The JAR with contracts should be taken from a remote location -->
    		<contractsMode>REMOTE</contractsMode>
    		<!-- ... additional configuration -->
    	</configuration>
    </plugin>
    Gradle
    contracts {
    	// We want to use the JAR with contracts with the following coordinates
    	// group id `com.example`, artifact id `beer-contracts`, LATEST version and NO classifier
    	contractDependency {
    		stringNotation = 'com.example:beer-contracts:+:'
    	}
    	// The JAR with contracts should be taken from a remote location
    	contractsMode = "REMOTE"
    	// Additional configuration
    }
  9. 合并包含新实现的生产者代码。

  10. CI 系统会:

    • 构建项目。

    • 生成测试、存根和存根 JAR。

    • 将包含应用程序和存根的 artifact 上传到 Nexus 或 Artifactory。

以下 UML 图显示了生产者流程:

flow-overview-consumer-cdc-external-producer