使用 Spring Cloud 合约

时间:2022-11-30 20:00:28

使用 Spring Cloud 合约

本节将更详细地介绍如何使用 Spring 云合约。它涵盖主题 比如如何使用春云合约的流程。我们也 介绍一些春季云合约最佳实践。

如果您刚开始使用 Spring Cloud 合约,则在深入研究之前,您可能应该阅读​​入门​​指南。 部分。

1. 在 Nexus 或 Artifactory 中使用存根进行提供商合同测试

您可以查看基于开发您的第一个 Spring 云合同的应用程序链接,以查看在 Nexus 或 Artifactory 流中使用存根进行提供商合同测试。

2. 在 Git 中使用存根进行提供程序合约测试

在此流程中,我们执行提供者合约测试(生产者不知道消费者如何使用他们的 API)。存根被上传到一个单独的仓库(它们不会上传到Artifactory或Nexus)。

2.1. 先决条件

在 git 中使用存根测试提供者合约之前,您必须提供 git 存储库 包含每个生产者的所有存根。有关此类项目的示例,请参阅此示例或此示例。 由于将存根推送到那里,存储库具有以下结构:

$ tree .
└── META-INF
└── folder.with.group.id.as.its.name
└── folder-with-artifact-id
└── folder-with-version
├── contractA.groovy
├── contractB.yml
└── contractC.groovy

您还必须提供设置了 Spring Cloud 合约存根运行程序的使用者代码。为 此类项目的示例,请参阅此示例并搜索 AET。您还必须提供具有 Spring Cloud 的生产者代码 合约设置,以及插件。有关此类项目的示例,请参阅此示例。​​BeerControllerGitTest​

2.2. 流程

该流程看起来与开发您的第一个基于Spring Cloud合同的应用程序中所述的流程完全相同, 但是实现是一个 git 存储库。​​Stub Storage​

您可以阅读有关设置 git 存储库以及设置消费者端和生产者端的更多信息 在文档的操作方法页面中。

2.3. 消费者设置

为了从 git 存储库而不是 Nexus 或 Artifactory 获取存根,您需要 需要在存根运行器的属性的 URL 中使用协议。 以下示例演示如何设置它:​​git​​​​repositoryRoot​

注解

JUnit 4 规则

JUnit 5 扩展

@AutoConfigureStubRunner(
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = "git://git@github.com:spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git",
ids = "com.example:artifact-id:0.0.1")

2.4. 设置生产者

要将存根推送到 git 存储库而不是 Nexus 或 Artifactory,您需要 在插件设置的 URL 中使用协议。你还需要明确告诉 在构建过程结束时推送存根的插件。以下示例显示 如何在Maven和Gradle中执行此操作:​​git​

<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<extensions>true</extensions>
<configuration>
<!-- Base class mappings etc. -->

<!-- We want to pick contracts from a Git repository -->
<contractsRepositoryUrl>git://git://git@github.com:spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git</contractsRepositoryUrl>

<!-- We reuse the contract dependency section to set up the path
to the folder that contains the contract definitions. In our case the
path will be /groupId/artifactId/version/contracts -->
<contractDependency>
<groupId>${project.groupId}</groupId>
<artifactId>${project.artifactId}</artifactId>
<version>${project.version}</version>
</contractDependency>

<!-- The contracts mode can't be classpath -->
<contractsMode>REMOTE</contractsMode>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<!-- By default we will not push the stubs back to SCM,
you have to explicitly add it as a goal -->
<goal>pushStubsToScm</goal>
</goals>
</execution>
</executions>
</plugin>

您可以在文档的“如何”部分中阅读有关设置 git 存储库的更多信息。

3. 消费者驱动的合同与生产者方的合同

查看消费者驱动的分步指南 合同 (CDC) 与生产者方的合同,以查看消费者驱动的合同 与生产者方面的合同流程。

4. 消费者驱动的合同与外部存储库中的合同

在此流程中,我们执行消费者驱动的合同测试。合同定义是 存储在单独的存储库中。

4.1. 先决条件

要将消费者驱动的合约与外部仓库中持有的合约一起使用,您需要设置一个 git 仓库,该仓库:

  • 包含每个生产者的所有协定定义。
  • 可以将协定定义打包到 JAR 中。
  • 对于每个合约生产者,包含一种安装存根的方法(例如,) 本地通过 Spring Cloud 合约插件(SCC 插件)。pom.xml

有关详细信息,请参阅操作方法部分。 我们描述了如何设置这样的存储库。 有关此类项目的示例,请参阅此示例。

您还需要设置了 Spring Cloud 合约存根运行程序的使用者代码。 有关此类项目的示例,请参阅此示例。 您还需要设置了 Spring 云合约的生产者代码以及插件。 有关此类项目的示例,请参阅此示例。 存根存储是 Nexus 或 Artifactory。

在高级别上,流程如下:

  1. 使用者使用单独存储库中的协定定义。
  2. 一旦消费者的工作完成,就会在消费者身上创建一个带有工作代码的分支 一端,并向保存合约定义的单独存储库发出拉取请求。
  3. 生产者使用合约将拉取请求接管到单独的存储库 定义并在本地安装带有所有协定的 JAR。
  4. 生产者从本地存储的 JAR 生成测试并写入缺失的 实现以使测试通过。
  5. 生产者的工作完成后,向保存 协定定义已合并。
  6. 在 CI 工具构建存储库后,使用协定定义和 JAR 合约定义上传到Nexus或Artifactory,生产者可以合并其分支。
  7. 最后,消费者可以切换到在线工作,从 远程位置,并且分支可以合并到主节点。

4.2. 消费者流

消费者:

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

    由于不存在服务器,测试失败。
  2. 克隆保存协定定义的存储库。
  3. 将需求设置为文件夹下的协定,使用者名称作为生产者的子文件夹。

    例如,对于命名的生产者和命名的使用者,合同将存储在producerconsumersrc/main/resources/contracts/producer/consumer/)
  4. 定义协定后,将生产者存根安装到本地存储,如以下示例所示:
$ cd src/main/resource/contracts/producer
$ ./mvnw clean install
  1. 在使用者测试中设置春季云合约 (SCC) 存根运行程序,以便:
  • 从本地存储中获取生产者存根。
  • 在每使用者存根模式下工作(这启用使用者驱动的合约模式)。

    SCC 存根运行程序:
  • 提取生产者存根。
  • 使用创建者存根运行内存中的 HTTP 服务器存根。 现在,您的测试与 HTTP 服务器存根通信,并且您的测试通过。
  • 使用合约定义创建对存储库的拉取请求,以及生产者的新协定。
  • 分支使用者代码,直到生成者团队合并其代码。

以下 UML 图显示了使用者流:

使用 Spring Cloud 合约

4.3. 生产者流程

制片人:

  1. 使用协定定义接管对存储库的拉取请求。你可以的 从命令行,如下所示
$ git checkout -b the_branch_with_pull_request master
git pull https://github.com/user_id/project_name.git the_branch_with_pull_request
  1. 安装协定定义,如下所示
$ ./mvnw clean install
  1. 设置插件以从 JAR 而不是从 JAR 获取合约定义,如下所示:​​src/test/resources/contracts​​马文
    格拉德尔
<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>
  1. 运行生成以生成测试和存根,如下所示:

    马文
    格拉德尔
./mvnw clean install
  1. 编写缺少的实现,以使测试通过。
  2. 将存储库的拉取请求与协定定义合并,如下所示:


$ 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 系统使用协定定义构建项目,并使用 Nexus或Artifactory的合同定义。

  1. 切换到远程工作。
  2. 设置插件,以便合约定义不再从本地获取 存储,但来自远程位置,如下所示:
<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>
  1. 将生产者代码与新实现合并。
  2. CI 系统:
  • 生成项目。
  • 生成测试、存根和存根 JAR。
  • 将带有应用程序和存根的项目上传到 Nexus 或 Artifactory。

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

使用 Spring Cloud 合约

5. 消费者驱动的合同与生产者方的合同,推送到 Git

您可以阅读消费者驱动合同分步指南 (CDC),其中合同在生产者端铺设,以查看消费者驱动的合同以及生产者端合同流程。

存根存储实现是一个 git 存储库。我们将在Git 中的存根进行提供程序合同测试部分中介绍其设置。

您可以在 中阅读有关为消费者和生产者端设置 git 存储库的更多信息 文档的操作方法部分。

6. 在Artifactory中使用非弹簧应用程序的存根进行供应商合同测试

6.1. 流程

您可以阅读开发您的第一个基于 Spring Cloud 合同的应用程序,以查看在 Nexus 或 Artifactory 中使用存根进行提供者合约测试的流程。

6.2. 设置消费者

对于使用者端,可以使用 JUnit 规则。这样,你就不需要启动 Spring 上下文。以下清单显示了这样的规则(在 JUnit4 和 JUnit 5 中);

JUnit 4 规则

JUnit 5 扩展

@Rule
public StubRunnerRule rule = new StubRunnerRule()
.downloadStub("com.example","artifact-id", "0.0.1")
.repoRoot("git://git@github.com:spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git")
.stubsMode(StubRunnerProperties.StubsMode.REMOTE);

6.3. 设置生产者

默认情况下,Spring 云合约插件使用 放心 的设置 生成的测试。由于非 Spring 应用程序不使用,因此您可以更改 thetoto 向绑定在特定端口的应用程序发送真实请求。​​MockMvc​​​​MockMvc​​​​testMode​​​​EXPLICIT​

在这个例子中,我们使用一个名为Javalin的框架来启动一个 非 Spring HTTP 服务器。

假设我们有以下应用程序:

package com.example.demo;

import io.javalin.Javalin;

public class DemoApplication {

public static void main(String[] args) {
new DemoApplication().run(7000);
}

public Javalin start(int port) {
return Javalin.create().start(port);
}

public Javalin registerGet(Javalin app) {
return app.get("/", ctx -> ctx.result("Hello World"));
}

public Javalin run(int port) {
return registerGet(start(port));
}

}

给定该应用程序,我们可以设置插件以使用模式(即 向真实端口发送请求),如下所示:​​EXPLICIT​

马文

格拉德尔

<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<extensions>true</extensions>
<configuration>
<baseClassForTests>com.example.demo.BaseClass</baseClassForTests>
<!-- This will setup the EXPLICIT mode for the tests -->
<testMode>EXPLICIT</testMode>
</configuration>
</plugin>

The base class might resemble the following:

import io.javalin.Javalin;
import io.restassured.RestAssured;
import org.junit.After;
import org.junit.Before;
import org.springframework.util.SocketUtils;

public class BaseClass {

Javalin app;

@Before
public void setup() {
// pick a random port
int port = SocketUtils.findAvailableTcpPort();
// start the application at a random port
this.app = start(port);
// tell Rest Assured where the started application is
RestAssured.baseURI = "http://localhost:" + port;
}

@After
public void close() {
// stop the server after each test
this.app.stop();
}

private Javalin start(int port) {
// reuse the production logic to start a server
return new DemoApplication().run(port);
}
}

通过这样的设置:

  • 我们已经设置了春云合约插件,使用模式发送真实 请求而不是嘲笑的请求。EXPLICIT
  • 我们定义了一个基类,它:
  • 在每个测试的随机端口上启动 HTTP 服务器。
  • 设置“放心”以向该端口发送请求。
  • 每次测试后关闭 HTTP 服务器。

7. 在非 JVM 世界中使用工件工厂中的存根进行提供程序合约测试

在此流程中,我们假设:

  • API 生产者和 API 消费者是非 JVM 应用程序。
  • 协定定义是用 YAML 编写的。
  • 存根存储是Artifactory或Nexus。
  • Spring Cloud Contract Docker (SCC Docker) 和 Spring Cloud Contract Stub Runner Docker (SCC 存根运行器Docker)图像。

你可以在这里阅读更多关于如何将Spring Cloud Contract与Docker一起使用的信息。

在这里,您可以 阅读有关如何在多语言世界中使用 Spring Cloud Contract 的博客文章。

在这里,您可以找到 一个 NodeJS 应用程序的示例,该应用程序同时使用 Spring 云合约作为生产者和 消费者。

7.1. 生产者流程

在高层次上,生产者:

  1. 编写协定定义(例如,在 YAML 中)。
  2. 将生成工具设置为:
  1. 在给定端口上使用模拟服务启动应用程序。

    如果无法进行模拟,则可以设置基础结构并以有状态方式定义测试。
  2. 运行春云合约 Docker 镜像,并将正在运行的应用程序的端口作为环境变量传递。

SCC Docker 映像: * 从附加的卷生成测试。 * 针对正在运行的应用程序运行测试。

测试完成后,存根将上传到存根存储站点(例如 Artifactory 或 Git)。

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

使用 Spring Cloud 合约

7.2. 消费者流

在高层次上,消费者:

  1. 将生成工具设置为:
  • 启动 Spring Cloud 合约存根运行程序 Docker 映像并启动存根。

    环境变量配置:
  • 要获取的存根。
  • 存储库的位置。

    请注意:
  • 若要使用本地存储,还可以将其作为卷附加。
  • 需要公开运行存根的端口。
  1. 针对正在运行的存根运行应用程序测试。

以下 UML 图显示了使用者流:

使用 Spring Cloud 合约

8. 在 Nexus 或 Artifactory 中使用 REST 文档和存根进行提供程序合同测试

在此流程中,我们不使用 Spring 云合约插件来生成测试和存根。我们编写Spring RESTDocs,并从中自动生成存根。最后,我们设置构建以打包存根并将它们上传到存根存储站点 — 在我们的例子中是 Nexus 或 Artifactory。

8.1. 生产者流程

作为生产商,我们:

  1. 编写我们 API 的 RESTDocs 测试。
  2. 将 Spring Cloud 合约存根运行程序启动器添加到我们的构建 (),如下所示:​​spring-cloud-starter-contract-stub-runner​
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
  1. 我们设置了构建工具来打包我们的存根,如下所示:
<!-- pom.xml -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>stub</id>
<phase>prepare-package</phase>
<goals>
<goal>single</goal>
</goals>
<inherited>false</inherited>
<configuration>
<attach>true</attach>
<descriptors>
${basedir}/src/assembly/stub.xml
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>

<!-- src/assembly/stub.xml -->
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>stubs</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.build.directory}/generated-snippets/stubs</directory>
<outputDirectory>META-INF/${project.groupId}/${project.artifactId}/${project.version}/mappings</outputDirectory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
</fileSets>
</assembly>

现在,当我们运行测试时,存根会自动发布和打包。

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

使用 Spring Cloud 合约

8.2. 消费者流

由于使用者流不受用于生成存根的工具的影响,因此您可以阅读开发第一个基于 Spring Cloud 合约的应用程序,以查看在 Nexus 或 Artifactory 中使用存根进行提供者合约测试的使用者端的流程。