“作方法”指南

1. 为什么使用 Spring Cloud Contract?

Spring Cloud Contract 在多语言环境中运行良好。这个项目有很多 非常有趣的功能。其中相当多的功能肯定会使 Spring Cloud Contract Verifier 在消费者驱动合约市场上脱颖而出 (CDC) 工具。最有趣的功能包括:spring-doc.cadn.net.cn

2. 如何用 Groovy 以外的语言编写合约?

您可以在 YAML 中编写 Contract。有关更多信息,请参阅此部分spring-doc.cadn.net.cn

我们正在努力允许更多方式来描述合约。您可以查看 github-issues 了解更多信息。spring-doc.cadn.net.cn

3. 如何为合约提供动态值?

与 stub 相关的最大挑战之一是它们的可重用性。只有它们能够得到广泛使用,它们才能达到其目的。 请求和响应元素的硬编码值(例如日期和 ID)通常会使这变得困难。 请考虑以下 JSON 请求:spring-doc.cadn.net.cn

{
    "time" : "2016-10-10 20:10:15",
    "id" : "9febab1c-6f36-4a0b-88d6-3b6a6d81cd4a",
    "body" : "foo"
}

现在考虑以下 JSON 响应:spring-doc.cadn.net.cn

{
    "time" : "2016-10-10 21:10:15",
    "id" : "c4231e1f-3ca9-48d3-b7e7-567d55f0d051",
    "body" : "bar"
}

想象一下,设置time字段(假设此内容由 database) 通过更改系统中的 clock 或通过提供数据提供程序的存根实现。同样是相关的 到id田。您可以创建 UUID 生成器的存根实现,但这样做几乎没有意义。spring-doc.cadn.net.cn

因此,作为使用者,您希望发送与任何形式的时间或任何 UUID 匹配的请求。这样,您的系统 像往常一样工作,无需您存根任何内容即可生成数据。假设,在上述情况下 JSON 中,最重要的部分是body田。您可以专注于此并为其他字段提供匹配。换句话说, 您希望存根的工作方式如下:spring-doc.cadn.net.cn

{
    "time" : "SOMETHING THAT MATCHES TIME",
    "id" : "SOMETHING THAT MATCHES UUID",
    "body" : "foo"
}

就响应而言,作为消费者,您需要一个可以作的具体价值。 因此,以下 JSON 有效:spring-doc.cadn.net.cn

{
    "time" : "2016-10-10 21:10:15",
    "id" : "c4231e1f-3ca9-48d3-b7e7-567d55f0d051",
    "body" : "bar"
}

在前面的部分中,我们从 Contract 生成了测试。因此,从生产商的角度来看,情况看起来 大不相同。我们解析提供的 Contract,并在测试中向你的 endpoints 发送一个真实的请求。 因此,对于请求的 producer 的情况,我们不能进行任何形式的匹配。我们需要具体的值,其中 producer 的后端可以工作。因此,以下 JSON 将有效:spring-doc.cadn.net.cn

{
    "time" : "2016-10-10 20:10:15",
    "id" : "9febab1c-6f36-4a0b-88d6-3b6a6d81cd4a",
    "body" : "foo"
}

另一方面,从合同有效性的角度来看,回应不一定必须 包含 的具体值timeid.假设您在生产者端生成这些 API。同样,您 必须进行大量 stub 以确保始终返回相同的值。这就是为什么从制片人的角度来看, 您可能需要以下响应:spring-doc.cadn.net.cn

{
    "time" : "SOMETHING THAT MATCHES TIME",
    "id" : "SOMETHING THAT MATCHES UUID",
    "body" : "bar"
}

那么,您如何为消费者提供匹配器,为生产者提供具体值(在其他时间则相反)? Spring Cloud Contract 允许您提供动态值。这意味着两者可能不同 沟通的双方。spring-doc.cadn.net.cn

您可以在 Contract DSL 部分阅读更多相关信息。spring-doc.cadn.net.cn

阅读与 JSON 相关的 Groovy 文档以了解如何 正确构建请求和响应正文。

4. 如何进行 Stubs 版本控制?

本节介绍存根的版本控制,您可以通过多种不同的方式进行处理:spring-doc.cadn.net.cn

4.1. API 版本控制

版本控制的真正含义是什么?如果参考 API 版本,则有 不同的方法:spring-doc.cadn.net.cn

我们不试图回答哪种方法更好的问题。你应该选择任何东西 满足您的需求,并让您产生业务价值。spring-doc.cadn.net.cn

假设您确实对 API 进行了版本控制。在这种情况下,您应该提供尽可能多的合同,以及您支持的版本。 您可以为每个版本创建一个子文件夹,也可以将其附加到合同名称 - 任何最适合您的方式。spring-doc.cadn.net.cn

4.2. JAR版本控制

如果版本控制是指包含存根的 JAR 版本,那么基本上有两种主要方法。spring-doc.cadn.net.cn

假设您执行持续交付和部署,这意味着您生成了新版本的 jar 并且 jar 可以随时投入生产。例如,您的 jar 版本 如下所示(因为它是在 2016 年 10 月 20 日 20:15:21 构建的):spring-doc.cadn.net.cn

1.0.0.20161020-201521-RELEASE

在这种情况下,您生成的存根 jar 应如下所示:spring-doc.cadn.net.cn

1.0.0.20161020-201521-RELEASE-stubs.jar

在这种情况下,您应该在application.yml@AutoConfigureStubRunner什么时候 引用存根,提供存根的最新版本。您可以通过传递 sign 来做到这一点。下面的示例演示如何执行此作:+spring-doc.cadn.net.cn

@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:8080"})

但是,如果版本控制是固定的(例如,1.0.4.RELEASE2.1.1),您必须设置 jar 的具体值 版本。以下示例显示了如何为版本 2.1.1 执行此作:spring-doc.cadn.net.cn

@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:2.1.1:stubs:8080"})

4.3. 开发或生产存根

您可以作分类器以针对当前开发版本运行测试 其他服务的存根或部署到生产环境的服务。如果您更改 您的构建使用prod-stubs分类器一旦你进入生产环境 部署中,您可以在一种情况下使用开发存根运行测试,在另一种情况下使用生产存根运行测试。spring-doc.cadn.net.cn

以下示例适用于使用存根的开发版本的测试:spring-doc.cadn.net.cn

@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:8080"})

以下示例适用于使用存根的生产版本的测试:spring-doc.cadn.net.cn

@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:prod-stubs:8080"})

您还可以在部署管道的属性中传递这些值。spring-doc.cadn.net.cn

5. 如何将公共仓库与合约一起使用,而不是将它们存储在 Producer 中?

另一种存储合同的方法,而不是将它们与生产者一起保存 他们在一个共同的地方。这种情况可能与安全问题有关(其中 使用者无法克隆创建者的代码)。此外,如果您将 Contract 保存在一个地方, 然后,作为生产者,您知道您有多少个使用者以及您可能会打破哪个使用者 替换为您的本地更改。spring-doc.cadn.net.cn

5.1. 仓库结构

假设我们有一个坐标为com.example:server和三个 消费者:client1,client2client3.然后,在具有 common 的 合约,您可以进行以下设置(您可以在此处查看)。 下面的清单显示了这样的结构:spring-doc.cadn.net.cn

├── com
│   └── example
│       └── server
│           ├── client1
│           │   └── expectation.groovy
│           ├── client2
│           │   └── expectation.groovy
│           ├── client3
│           │   └── expectation.groovy
│           └── pom.xml
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    └── assembly
        └── contracts.xml

在斜杠分隔的groupid/artifact id文件夹 (com/example/server),则您拥有 三个消费者的期望 (client1,client2client3).期望是标准的 Groovy DSL Contract 文件,如本文档中所述。此存储库必须生成一个 JAR 文件,该文件将 一对一到存储库的内容。spring-doc.cadn.net.cn

以下示例显示了pom.xml文件。server文件夹:spring-doc.cadn.net.cn

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>server</artifactId>
    <version>0.0.1</version>

    <name>Server Stubs</name>
    <description>POM used to install locally stubs for consumer side</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.15</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <spring-cloud-contract.version>3.1.10-SNAPSHOT</spring-cloud-contract.version>
        <spring-cloud-release.version>2021.0.10-SNAPSHOT</spring-cloud-release.version>
        <excludeBuildFolders>true</excludeBuildFolders>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud-release.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-contract-maven-plugin</artifactId>
                <version>${spring-cloud-contract.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <!-- By default it would search under src/test/resources/ -->
                    <contractsDirectory>${project.basedir}</contractsDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <!--<repository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/release</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>-->
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
        <!--<pluginRepository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/release</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>-->
    </pluginRepositories>

</project>

除了 Spring Cloud Contract Maven 插件之外,没有其他依赖项。 那些pom.xml文件是 Consumer 端运行所必需的mvn clean install -DskipTests本地安装 Producer 项目的存根。spring-doc.cadn.net.cn

pom.xml文件可能如下所示:spring-doc.cadn.net.cn

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example.standalone</groupId>
    <artifactId>contracts</artifactId>
    <version>0.0.1</version>

    <name>Contracts</name>
    <description>Contains all the Spring Cloud Contracts, well, contracts. JAR used by the
        producers to generate tests and stubs
    </description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <executions>
                    <execution>
                        <id>contracts</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <configuration>
                            <attach>true</attach>
                            <descriptor>${basedir}/src/assembly/contracts.xml</descriptor>
                            <!-- If you want an explicit classifier remove the following line -->
                            <appendAssemblyId>false</appendAssemblyId>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

它使用汇编插件来构建包含所有协定的 JAR。以下示例 显示了这样的设置:spring-doc.cadn.net.cn

<assembly xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 https://maven.apache.org/xsd/assembly-1.1.3.xsd">
    <id>project</id>
    <formats>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <fileSets>
        <fileSet>
            <directory>${project.basedir}</directory>
            <outputDirectory>/</outputDirectory>
            <useDefaultExcludes>true</useDefaultExcludes>
            <excludes>
                <exclude>**/${project.build.directory}/**</exclude>
                <exclude>mvnw</exclude>
                <exclude>mvnw.cmd</exclude>
                <exclude>.mvn/**</exclude>
                <exclude>src/**</exclude>
            </excludes>
        </fileSet>
    </fileSets>
</assembly>

5.2. 工作流程

该工作流假定 Spring Cloud Contract 同时在使用者和 producer 端。在公共存储库中还有适当的插件设置,其中 合同。CI 作业是为公共存储库设置的,以构建所有 合同并将其上传到 Nexus 或 Artifactory。下图显示了此 UML 工作流程:spring-doc.cadn.net.cn

如何常用 repo

5.3. 消费者

当消费者想要离线处理 Contract 而不是克隆 producer 时 code 中,消费者团队会克隆公共仓库,然后转到所需的生产者的 文件夹(例如com/example/server) 并运行mvn clean install -DskipTests自 在本地安装从 Contract 转换的 stub。spring-doc.cadn.net.cn

您需要在本地安装 Maven

5.4. 生产者

作为生产者,您可以更改 Spring Cloud Contract Verifier 以提供 URL 和 包含 Contract 的 JAR 的依赖项,如下所示:spring-doc.cadn.net.cn

<plugin>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    <configuration>
        <contractsMode>REMOTE</contractsMode>
        <contractsRepositoryUrl>
            https://link/to/your/nexus/or/artifactory/or/sth
        </contractsRepositoryUrl>
        <contractDependency>
            <groupId>com.example.standalone</groupId>
            <artifactId>contracts</artifactId>
        </contractDependency>
    </configuration>
</plugin>

通过此设置,带有groupidcom.example.standalone以及一个artifactidcontracts下载自link/to/your/nexus/or/artifactory/or/sth.是的 然后解压缩到本地临时文件夹中,并且com/example/server被选为用于生成测试和存根的文件。由于 根据此约定,生产者团队可以知道哪些 Consumer 团队在何时中断 进行了一些不兼容的更改。spring-doc.cadn.net.cn

流的其余部分看起来相同。spring-doc.cadn.net.cn

5.5. 如何定义每个主题而不是每个生产者的消息收发合同?

为避免公共存储库中的消息传递合同重复,当几个生产者将消息写入一个主题时, 我们可以创建一个结构,其中 REST 合同放置在每个生产者和消息传递的文件夹中 合同放置在每个主题的文件夹中。spring-doc.cadn.net.cn

5.5.1. 对于 Maven 项目

为了能够在生产者端工作,我们应该为 按我们感兴趣的消息主题筛选常见的存储库 JAR 文件。这includedFilesMaven Spring Cloud Contract 插件的属性 让我们这样做。也contractsPath需要指定,因为默认路径为 通用存储库groupid/artifactid.以下示例显示了一个 Maven Spring Cloud Contract 插件:spring-doc.cadn.net.cn

<plugin>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-contract-maven-plugin</artifactId>
   <version>${spring-cloud-contract.version}</version>
   <configuration>
      <contractsMode>REMOTE</contractsMode>
      <contractsRepositoryUrl>https://link/to/your/nexus/or/artifactory/or/sth</contractsRepositoryUrl>
      <contractDependency>
         <groupId>com.example</groupId>
         <artifactId>common-repo-with-contracts</artifactId>
         <version>+</version>
      </contractDependency>
      <contractsPath>/</contractsPath>
      <baseClassMappings>
         <baseClassMapping>
            <contractPackageRegex>.*messaging.*</contractPackageRegex>
            <baseClassFQN>com.example.services.MessagingBase</baseClassFQN>
         </baseClassMapping>
         <baseClassMapping>
            <contractPackageRegex>.*rest.*</contractPackageRegex>
            <baseClassFQN>com.example.services.TestBase</baseClassFQN>
         </baseClassMapping>
      </baseClassMappings>
      <includedFiles>
         <includedFile>**/${project.artifactId}/**</includedFile>
         <includedFile>**/${first-topic}/**</includedFile>
         <includedFile>**/${second-topic}/**</includedFile>
      </includedFiles>
   </configuration>
</plugin>
上述 Maven 插件中的许多值都可以更改。我们将其包括 说明目的,而不是试图提供一个 “典型 ”的例子。

5.5.2. 对于 Gradle 项目

要使用 Gradle 项目,请执行以下作:spring-doc.cadn.net.cn

  1. 为常见的存储库依赖项添加自定义配置,如下所示:spring-doc.cadn.net.cn

    ext {
        contractsGroupId = "com.example"
        contractsArtifactId = "common-repo"
        contractsVersion = "1.2.3"
    }
    
    configurations {
        contracts {
            transitive = false
        }
    }
    
  2. 将 common repository 依赖项添加到您的 Classpath 中,如下所示:spring-doc.cadn.net.cn

    dependencies {
        contracts "${contractsGroupId}:${contractsArtifactId}:${contractsVersion}"
        testCompile "${contractsGroupId}:${contractsArtifactId}:${contractsVersion}"
    }
    
  3. 将依赖项下载到相应的文件夹,如下所示:spring-doc.cadn.net.cn

    task getContracts(type: Copy) {
        from configurations.contracts
        into new File(project.buildDir, "downloadedContracts")
    }
    
  4. 解压缩 JAR,如下所示:spring-doc.cadn.net.cn

    task unzipContracts(type: Copy) {
        def zipFile = new File(project.buildDir, "downloadedContracts/${contractsArtifactId}-${contractsVersion}.jar")
        def outputDir = file("${buildDir}/unpackedContracts")
    
        from zipTree(zipFile)
        into outputDir
    }
    
  5. 清理未使用的合约,如下所示:spring-doc.cadn.net.cn

    task deleteUnwantedContracts(type: Delete) {
        delete fileTree(dir: "${buildDir}/unpackedContracts",
            include: "**/*",
            excludes: [
                "**/${project.name}/**"",
                "**/${first-topic}/**",
                "**/${second-topic}/**"])
    }
    
  6. 创建任务依赖关系,如下所示:spring-doc.cadn.net.cn

    unzipContracts.dependsOn("getContracts")
    deleteUnwantedContracts.dependsOn("unzipContracts")
    build.dependsOn("deleteUnwantedContracts")
    
  7. 通过指定包含 Contract 的目录来配置插件,方法是将 这contractsDslDir属性,如下所示:spring-doc.cadn.net.cn

    contracts {
        contractsDslDir = new File("${buildDir}/unpackedContracts")
    }
    

6. 如何使用 Git 作为合约和存根的存储?

在多语言世界中,有些语言不使用二进制存储,如 Artifactory 和 Nexus 可以。从 Spring Cloud Contract 版本 2.0.0 开始,我们提供 在 SCM (Source Control Management) 存储库中存储合同和存根的机制。目前, 唯一支持的 SCM 是 Git。spring-doc.cadn.net.cn

存储库必须具有以下设置 (您可以从此处查看):spring-doc.cadn.net.cn

.
└── META-INF
    └── com.example
        └── beer-api-producer-git
            └── 0.0.1-SNAPSHOT
                ├── contracts
                │   └── beer-api-consumer
                │       ├── messaging
                │       │   ├── shouldSendAcceptedVerification.groovy
                │       │   └── shouldSendRejectedVerification.groovy
                │       └── rest
                │           ├── shouldGrantABeerIfOldEnough.groovy
                │           └── shouldRejectABeerIfTooYoung.groovy
                └── mappings
                    └── beer-api-consumer
                        └── rest
                            ├── shouldGrantABeerIfOldEnough.json
                            └── shouldRejectABeerIfTooYoung.json

META-INF文件夹:spring-doc.cadn.net.cn

  • 我们按以下方式对应用程序进行分组groupId(例如com.example).spring-doc.cadn.net.cn

  • 每个应用程序都由其artifactId(例如,beer-api-producer-git).spring-doc.cadn.net.cn

  • 接下来,每个应用程序都按其版本(例如0.0.1-SNAPSHOT).开始 从 Spring Cloud Contract 版本2.1.0中,您可以指定版本,如下所示 (假设您的版本遵循语义版本控制):spring-doc.cadn.net.cn

最后,有两个文件夹:spring-doc.cadn.net.cn

  • contracts:好的做法是存储每个 consumer 中具有 consumer 名称(例如beer-api-consumer).这样,您 可以使用stubs-per-consumer特征。进一步的目录结构是任意的。spring-doc.cadn.net.cn

  • mappings:Maven 或 Gradle Spring Cloud Contract 插件推送 此文件夹中的存根服务器映射。在使用者端,Stub Runner 会扫描此文件夹 以启动具有存根定义的存根服务器。文件夹结构是一个副本 在contracts子文件夹。spring-doc.cadn.net.cn

6.1. 协议约定

要控制合约来源的类型和位置(无论 binary storage 或 SCM 存储库),您可以在 存储库。Spring Cloud Contract 迭代已注册的协议解析器 并尝试获取 Contract (通过使用插件) 或 stub (从 Stub Runner)。spring-doc.cadn.net.cn

对于 SCM 功能,目前我们支持 Git 存储库。要使用它, 在需要放置存储库 URL 的属性中,您必须为存储库 URL 添加前缀 连接 URL 与git://.下面的清单显示了一些示例:spring-doc.cadn.net.cn

git://file:///foo/bar
git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git
git://[email protected]:spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git

6.2. 生产者

对于生产者,要使用 SCM(源代码控制管理)方法,我们可以重用 我们用于外部合约的机制相同。我们路由 Spring Cloud Contract 使用以 这git://协议。spring-doc.cadn.net.cn

您必须手动添加pushStubsToScmgoal 或使用 (bind)pushStubsToScmtask 中 Gradle 的 Gradle 中。我们不会将存根推送到origin你的 git 存储 库。

以下清单包括 Maven 和 Gradle 构建文件的相关部分:spring-doc.cadn.net.cn

Maven 系列
<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://https://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>
Gradle
contracts {
    // We want to pick contracts from a Git repository
    contractDependency {
        stringNotation = "${project.group}:${project.name}:${project.version}"
    }
    /*
    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
     */
    contractRepository {
        repositoryUrl = "git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git"
    }
    // The mode can't be classpath
    contractsMode = "REMOTE"
    // Base class mappings etc.
}

/*
In this scenario we want to publish stubs to SCM whenever
the `publish` task is invoked
*/
publish.dependsOn("publishStubsToScm")

您还可以进一步自定义publishStubsToScmgradle 任务。在以下示例中, 该任务经过自定义,可从本地 Git 存储库中选择 Contract:spring-doc.cadn.net.cn

Gradle
publishStubsToScm {
    // We want to modify the default set up of the plugin when publish stubs to scm is called
    // We want to pick contracts from a Git repository
    contractDependency {
        stringNotation = "${project.group}:${project.name}:${project.version}"
    }
    /*
    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
     */
    contractRepository {
        repositoryUrl = "git://file://${new File(project.rootDir, "../target")}/contract_empty_git/"
    }
    // We set the contracts mode to `LOCAL`
    contractsMode = "LOCAL"
    }
重要

2.3.0.RELEASEcustomize{}之前用于publishStubsToScm自定义不再可用。应直接应用设置 在publishStubsToScmclosure 的 Curve 方法,如前面的示例所示。spring-doc.cadn.net.cn

使用这样的设置:spring-doc.cadn.net.cn

6.3. 本地存储 Contract 的 producer

使用 SCM 作为存根和 Contract 目标的另一种选择是将 与生产者在本地签订合同,并且仅将合同和存根推送到 SCM。 下面的清单显示了使用 Maven 和 Gradle 实现此目的所需的设置:spring-doc.cadn.net.cn

Maven 系列
<plugin>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    <version>${spring-cloud-contract.version}</version>
    <extensions>true</extensions>
    <!-- In the default configuration, we want to use the contracts stored locally -->
    <configuration>
        <baseClassMappings>
            <baseClassMapping>
                <contractPackageRegex>.*messaging.*</contractPackageRegex>
                <baseClassFQN>com.example.BeerMessagingBase</baseClassFQN>
            </baseClassMapping>
            <baseClassMapping>
                <contractPackageRegex>.*rest.*</contractPackageRegex>
                <baseClassFQN>com.example.BeerRestBase</baseClassFQN>
            </baseClassMapping>
        </baseClassMappings>
        <basePackageForTests>com.example</basePackageForTests>
    </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>
            <configuration>
                <!-- We want to pick contracts from a Git repository -->
                <contractsRepositoryUrl>git://file://${env.ROOT}/target/contract_empty_git/
                </contractsRepositoryUrl>
                <!-- Example of URL via git protocol -->
                <!--<contractsRepositoryUrl>git://[email protected]:spring-cloud-samples/spring-cloud-contract-samples.git</contractsRepositoryUrl>-->
                <!-- Example of URL via http protocol -->
                <!--<contractsRepositoryUrl>git://https://github.com/spring-cloud-samples/spring-cloud-contract-samples.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 mode can't be classpath -->
                <contractsMode>LOCAL</contractsMode>
            </configuration>
        </execution>
    </executions>
</plugin>
Gradle
contracts {
    testFramework = "JUNIT5"
        // Base package for generated tests
    basePackageForTests = "com.example"
    baseClassMappings {
        baseClassMapping(".*messaging.*", "com.example.BeerMessagingBase")
        baseClassMapping(".*rest.*", "com.example.BeerRestBase")
    }

    /*
    In this scenario we want to publish stubs to SCM whenever
    the `publish` task is executed
    */
    publishStubsToScm {

        // We want to pick contracts from a Git repository
        contractDependency {
            stringNotation = "${project.group}:${project.name}:${project.version}"
        }

        contractRepository {
            /*
            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
             */
            repositoryUrl = "git://file://${new File(project.rootDir, "../target")}/contract_empty_git/"
        }
    }
}

使用这样的设置:spring-doc.cadn.net.cn

6.4. 将与 Producer 的 Contract 和存根保存在外部存储库中

您还可以将合同保留在创建者存储库中,但将存根保留在外部 git 存储库中。 当您想使用基本消费者-生产者协作流但不能使用时,这最有用 使用构件存储库存储存根。spring-doc.cadn.net.cn

为此,请使用通常的 producer 设置,然后添加pushStubsToScm目标和设置contractsRepositoryUrl添加到要保留存根的存储库中。spring-doc.cadn.net.cn

6.5. 消费者

在消费者端,当传递repositoryRoot参数 要么来自@AutoConfigureStubRunnerannotation、 JUnit 4 规则、JUnit 5 扩展或属性,则可以传递 SCM 存储库,前缀为git://协议。以下示例显示了如何执行此作:spring-doc.cadn.net.cn

@AutoConfigureStubRunner(
    stubsMode="REMOTE",
    repositoryRoot="git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git",
    ids="com.example:bookstore:0.0.1.RELEASE"
)

使用这样的设置:spring-doc.cadn.net.cn

7. 如何使用 Pact Broker?

使用 Pact 时,您可以使用 Pact Broker 来存储和共享 Pact 定义。从 Spring Cloud Contract 开始 2.0.0 中,您可以从 Pact Broker 获取 Pact 文件以生成 测试和存根。spring-doc.cadn.net.cn

Pact 遵循消费者契约约定。这意味着 使用者首先创建 Pact 定义,然后 与 Producer 共享文件。这些期望是产生的 从 Consumer 的代码中,如果期望 未得到满足。

7.1. 如何使用 Pact

Spring Cloud Contract 包括对 Pact 表示的支持 Contract 直到版本 4。您可以使用 Pact 文件,而不是使用 DSL。在本节中,我们将 演示如何为项目添加 Pact 支持。但请注意,并非所有功能都受支持。 从版本 3 开始,你可以为同一个元素组合多个匹配器: 你可以对 body、headers、request 和 path 使用 matchers,也可以使用值生成器。 Spring Cloud Contract 目前仅支持使用AND规则逻辑。 接下来,在转换过程中会跳过 request 和 path matcher。 当使用具有给定格式的日期、时间或日期时间值生成器时, 跳过给定的格式,并使用 ISO 格式。spring-doc.cadn.net.cn

7.2. 契约转换器

为了正确支持 Spring Cloud Contract 的消息传递方式 使用 Pact,您必须提供一些额外的元数据条目。spring-doc.cadn.net.cn

要定义消息发送到的目标,您必须 设置metaData条目,其中sentTokey 等于目标 要发送的消息(例如"metaData": { "sentTo": "activemq:output" }).spring-doc.cadn.net.cn

7.3. 契约合约

Spring Cloud Contract 可以读取 Pact JSON 定义。您可以将文件放在src/test/resources/contracts文件夹。记得把spring-cloud-contract-pact依赖项。spring-doc.cadn.net.cn

7.4. 生产者协议

在生产者方面,您必须向插件添加两个额外的依赖项 配置。一个是 Spring Cloud Contract Pact 支持,另一个表示 您使用的当前 Pact 版本。下面的清单显示了如何对两者执行此作 Maven 和 Gradle:spring-doc.cadn.net.cn

Maven 系列
<plugin>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    <version>${spring-cloud-contract.version}</version>
    <extensions>true</extensions>
    <configuration>
        <testFramework>JUNIT5</testFramework>
        <contractsRepositoryUrl>pact://http://localhost:8085</contractsRepositoryUrl>
        <contractDependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>${project.artifactId}</artifactId>
            <version>+</version>
        </contractDependency>
        <contractsMode>REMOTE</contractsMode>
        <baseClassForTests>com.example.BeerRestBase</baseClassForTests>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-contract-pact</artifactId>
            <version>${spring-cloud-contract.version}</version>
        </dependency>
    </dependencies>
</plugin>
Gradle
    testImplementation("org.springframework.cloud:spring-cloud-contract-pact")
contracts {
    testFramework = "JUNIT5"
    contractDependency {
        stringNotation = "${project.group}:${project.name}:+"
    }
    contractsMode = "REMOTE"
    // Base package for generated tests
    baseClassForTests = "com.example.BeerRestBase"
    contractRepository {
        repositoryUrl = "pact://http://localhost:8085"
    }
}

构建应用程序时,将生成 test 和 stub。以下内容 example 显示了来自此进程的测试和存根:spring-doc.cadn.net.cn

测试
@Test
    public void validate_shouldMarkClientAsFraud() throws Exception {
        // given:
            MockMvcRequestSpecification request = given()
                    .header("Content-Type", "application/vnd.fraud.v1+json")
                    .body("{\"clientId\":\"1234567890\",\"loanAmount\":99999}");

        // when:
            ResponseOptions response = given().spec(request)
                    .put("/fraudcheck");

        // then:
            assertThat(response.statusCode()).isEqualTo(200);
            assertThat(response.header("Content-Type")).matches("application/vnd\\.fraud\\.v1\\+json.*");
        // and:
            DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
            assertThatJson(parsedJson).field("['rejectionReason']").isEqualTo("Amount too high");
        // and:
            assertThat(parsedJson.read("$.fraudCheckStatus", String.class)).matches("FRAUD");
    }
存根
{
  "id" : "996ae5ae-6834-4db6-8fac-358ca187ab62",
  "uuid" : "996ae5ae-6834-4db6-8fac-358ca187ab62",
  "request" : {
    "url" : "/fraudcheck",
    "method" : "PUT",
    "headers" : {
      "Content-Type" : {
        "matches" : "application/vnd\\.fraud\\.v1\\+json.*"
      }
    },
    "bodyPatterns" : [ {
      "matchesJsonPath" : "$[?(@.['loanAmount'] = 99999)]"
    }, {
      "matchesJsonPath" : "$[?(@.clientId =~ /([0-9]{10})/)]"
    } ]
  },
  "response" : {
    "status" : 200,
    "body" : "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}",
    "headers" : {
      "Content-Type" : "application/vnd.fraud.v1+json;charset=UTF-8"
    },
    "transformers" : [ "response-template" ]
  },
}

7.5. 消费者协议

在使用者端,您必须向项目添加两个额外的依赖项 依赖。一个是 Spring Cloud Contract Pact 支持,另一个表示 您使用的当前 Pact 版本。下面的清单显示了如何对两者执行此作 Maven 和 Gradle:spring-doc.cadn.net.cn

Maven 系列
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-pact</artifactId>
    <scope>test</scope>
</dependency>
Gradle
testImplementation("org.springframework.cloud:spring-cloud-starter-contract-stub-runner")
testImplementation("org.springframework.cloud:spring-cloud-contract-pact")

7.6. 与 Pact Broker 通信

每当repositoryRootproperty 以 Pact 协议开头 (即,以pact://),则存根下载程序会尝试 从 Pact Broker 获取 Pact 合同定义。 之后设置的任何内容pact://解析为 Pact Broker URL。spring-doc.cadn.net.cn

通过设置环境变量、系统属性或设置的属性 在 Plugin 或 Contract 存储库配置中,您可以 调整 Downloader 的行为。下表描述了 性能:spring-doc.cadn.net.cn

表 1.Pact Stub Downloader 属性

属性的名称spring-doc.cadn.net.cn

违约spring-doc.cadn.net.cn

描述spring-doc.cadn.net.cn

* pactbroker.host(插件属性)spring-doc.cadn.net.cn

* stubrunner.properties.pactbroker.host(系统属性)spring-doc.cadn.net.cn

* STUBRUNNER_PROPERTIES_PACTBROKER_HOST(env 属性)spring-doc.cadn.net.cn

Host from URL 传递到repositoryRootspring-doc.cadn.net.cn

Pact Broker 的 URL。spring-doc.cadn.net.cn

* pactbroker.port(插件属性)spring-doc.cadn.net.cn

* stubrunner.properties.pactbroker.port(系统属性)spring-doc.cadn.net.cn

* STUBRUNNER_PROPERTIES_PACTBROKER_PORT(env 属性)spring-doc.cadn.net.cn

从 URL 传递到的端口repositoryRootspring-doc.cadn.net.cn

Pact Broker 的端口。spring-doc.cadn.net.cn

* pactbroker.protocol(插件属性)spring-doc.cadn.net.cn

* stubrunner.properties.pactbroker.protocol(系统属性)spring-doc.cadn.net.cn

* STUBRUNNER_PROPERTIES_PACTBROKER_PROTOCOL(env 属性)spring-doc.cadn.net.cn

从 URL 传递到的协议repositoryRootspring-doc.cadn.net.cn

Pact Broker 的协议。spring-doc.cadn.net.cn

* pactbroker.tags(插件属性)spring-doc.cadn.net.cn

* stubrunner.properties.pactbroker.tags(系统属性)spring-doc.cadn.net.cn

* STUBRUNNER_PROPERTIES_PACTBROKER_TAGS(env 属性)spring-doc.cadn.net.cn

存根的版本,或latest如果 version 为+spring-doc.cadn.net.cn

应该用于获取存根的标记。spring-doc.cadn.net.cn

* pactbroker.auth.scheme(插件属性)spring-doc.cadn.net.cn

* stubrunner.properties.pactbroker.auth.scheme(系统属性)spring-doc.cadn.net.cn

* STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_SCHEME(env 属性)spring-doc.cadn.net.cn

Basicspring-doc.cadn.net.cn

应用于连接到 Pact Broker 的身份验证类型。spring-doc.cadn.net.cn

* pactbroker.auth.username(插件属性)spring-doc.cadn.net.cn

* stubrunner.properties.pactbroker.auth.username(系统属性)spring-doc.cadn.net.cn

* STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_USERNAME(env 属性)spring-doc.cadn.net.cn

传递给contractsRepositoryUsername(Maven) 或contractRepository.username(Gradle)spring-doc.cadn.net.cn

连接到 Pact Broker 时使用的用户名。spring-doc.cadn.net.cn

* pactbroker.auth.password(插件属性)spring-doc.cadn.net.cn

* stubrunner.properties.pactbroker.auth.password(系统属性)spring-doc.cadn.net.cn

* STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_PASSWORD(env 属性)spring-doc.cadn.net.cn

传递给contractsRepositoryPassword(Maven) 或contractRepository.password(Gradle)spring-doc.cadn.net.cn

连接到 Pact Broker 时使用的密码。spring-doc.cadn.net.cn

* pactbroker.provider-name-with-group-id(插件属性)spring-doc.cadn.net.cn

* stubrunner.properties.pactbroker.provider-name-with-group-id(系统属性)spring-doc.cadn.net.cn

* STUBRUNNER_PROPERTIES_PACTBROKER_PROVIDER_NAME_WITH_GROUP_ID(env 属性)spring-doc.cadn.net.cn

spring-doc.cadn.net.cn

什么时候true,则提供程序名称是groupId:artifactId.如果falseartifactId被使用。spring-doc.cadn.net.cn

7.7. 流:在消费者端使用 Pact Broker 的消费者合同方法

使用者使用 Pact 框架生成 Pact 文件。这 Pact 文件将发送到 Pact Broker。您可以在此处找到此类设置的示例。spring-doc.cadn.net.cn

7.8. 流:在生产者端使用 Pact Broker 的消费者合同方法

为了让创建者使用 Pact Broker 中的 Pact 文件,我们可以重用 我们用于外部合约的机制相同。我们路由 Spring Cloud Contract 将 Pact 实现与包含 这pact://协议。您可以将 URL 传递给 Pact 经纪人。您可以在此处找到此类设置的示例。 以下清单显示了 Maven 和 Gradle 的配置详细信息:spring-doc.cadn.net.cn

Maven 系列
<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>pact://http://localhost:8085</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>
            <!-- When + is passed, a latest tag will be applied when fetching pacts -->
            <version>+</version>
        </contractDependency>

        <!-- The contracts mode can't be classpath -->
        <contractsMode>REMOTE</contractsMode>
    </configuration>
    <!-- Don't forget to add spring-cloud-contract-pact to the classpath! -->
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-contract-pact</artifactId>
            <version>${spring-cloud-contract.version}</version>
        </dependency>
    </dependencies>
</plugin>
Gradle
buildscript {
    repositories {
        //...
    }

    dependencies {
        // ...
        // Don't forget to add spring-cloud-contract-pact to the classpath!
        classpath "org.springframework.cloud:spring-cloud-contract-pact:${contractVersion}"
    }
}

contracts {
    // When + is passed, a latest tag will be applied when fetching pacts
    contractDependency {
        stringNotation = "${project.group}:${project.name}:+"
    }
    contractRepository {
        repositoryUrl = "pact://http://localhost:8085"
    }
    // The mode can't be classpath
    contractsMode = "REMOTE"
    // Base class mappings etc.
}

使用这样的设置:spring-doc.cadn.net.cn

7.9. 流程:消费者端有 Pact 的生产者合同方法

在不想采用消费者契约方法的情况下 (对于每个消费者,定义期望)但您更喜欢 要执行 producer 合约(producer 提供 Contract 和 publishes stubs),则可以将 Spring Cloud Contract 与 Stub Runner 选项。您可以在此处找到此类设置的示例。spring-doc.cadn.net.cn

请记住添加 Stub Runner 和 Spring Cloud Contract Pact 模块 作为测试依赖项。spring-doc.cadn.net.cn

以下清单显示了 Maven 和 Gradle 的配置详细信息:spring-doc.cadn.net.cn

Maven 系列
<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>

<!-- Don't forget to add spring-cloud-contract-pact to the classpath! -->
<dependencies>
    <!-- ... -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-contract-pact</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
Gradle
dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

dependencies {
    //...
    testCompile("org.springframework.cloud:spring-cloud-starter-contract-stub-runner")
    // Don't forget to add spring-cloud-contract-pact to the classpath!
    testCompile("org.springframework.cloud:spring-cloud-contract-pact")
}

接下来,您可以将 Pact Broker 的 URL 传递给repositoryRoot前缀 使用pact://协议(例如pact://http://localhost:8085),如下所示 示例显示:spring-doc.cadn.net.cn

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.REMOTE,
        ids = "com.example:beer-api-producer-pact",
        repositoryRoot = "pact://http://localhost:8085")
public class BeerControllerTest {
    //Inject the port of the running stub
    @StubRunnerPort("beer-api-producer-pact") int producerPort;
    //...
}

使用这样的设置:spring-doc.cadn.net.cn

8. 如何调试生成的测试客户端发送的请求/响应?

生成的测试都以某种形式或方式归结为 RestAssured。放心 依赖于 Apache HttpClient。 HttpClient 有一个叫做线路日志记录的工具。 它将整个请求和响应记录到 HttpClient。Spring Boot 具有用于执行此类作的 logging 通用应用程序属性。要使用它,请将其添加到您的应用程序属性中,如下所示:spring-doc.cadn.net.cn

logging.level.org.apache.http.wire=DEBUG

9. 如何调试 WireMock 发送的映射、请求或响应?

从版本开始1.2.0,我们将 WireMock 日志记录设置为info并将 WireMock 通知程序设置为 verbose。现在您可以 确切地知道 WireMock 服务器收到了什么请求以及哪个请求 选择了匹配的响应定义。spring-doc.cadn.net.cn

要关闭此功能,请将 WireMock 日志记录设置为ERROR如下:spring-doc.cadn.net.cn

logging.level.com.github.tomakehurst.wiremock=ERROR

10. 如何查看在 HTTP 服务器存根中注册的内容?

您可以使用mappingsOutputFolder属性@AutoConfigureStubRunner,StubRunnerRuleStubRunnerExtension转储每个工件 ID 的所有映射。此外,给定存根服务器的端口 已启动。spring-doc.cadn.net.cn

11. 如何从文件中引用文本?

在 1.2.0 版本中,我们添加了此功能。您可以调用file(…​)方法中的 DSL 并提供相对于 Contract 所在位置的路径。 如果您使用 YAML,则可以使用bodyFromFile财产。spring-doc.cadn.net.cn

12. 如何从 Spring Cloud Contract Contract 生成 Pact、YAML 或 X 文件?

Spring Cloud Contract 附带一个ToFileContractsTransformer类,它允许您转储 contracts 作为给定ContractConverter.它包含一个static void main方法,该方法允许您将转换器作为可执行文件运行。它需要以下内容 参数:spring-doc.cadn.net.cn

  • 参数 1 :FQN:的完全限定名称ContractConverter(例如,PactContractConverter).必需。spring-doc.cadn.net.cn

  • 参数 2 :path:应存储转储文件的路径。OPTIONAL — 默认为target/converted-contracts.spring-doc.cadn.net.cn

  • 参数 3 :path:应搜索合同的路径。OPTIONAL — 默认为src/test/resources/contracts.spring-doc.cadn.net.cn

调用 transformer 后,将处理 Spring Cloud Contract 文件,并且 取决于提供的 FQNContractTransformer,则合同将转换 转换为所需的格式并转储到提供的文件夹中。spring-doc.cadn.net.cn

以下示例显示了如何为 Maven 和 Gradle 配置 Pact 集成:spring-doc.cadn.net.cn

Maven 系列
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.6.0</version>
    <executions>
        <execution>
            <id>convert-dsl-to-pact</id>
            <phase>process-test-classes</phase>
            <configuration>
                <classpathScope>test</classpathScope>
                <mainClass>
                    org.springframework.cloud.contract.verifier.util.ToFileContractsTransformer
                </mainClass>
                <arguments>
                    <argument>
                        org.springframework.cloud.contract.verifier.spec.pact.PactContractConverter
                    </argument>
                    <argument>${project.basedir}/target/pacts</argument>
                    <argument>
                        ${project.basedir}/src/test/resources/contracts
                    </argument>
                </arguments>
            </configuration>
            <goals>
                <goal>java</goal>
            </goals>
        </execution>
    </executions>
</plugin>
Gradle
task convertContracts(type: JavaExec) {
    main = "org.springframework.cloud.contract.verifier.util.ToFileContractsTransformer"
    classpath = sourceSets.test.compileClasspath
    args("org.springframework.cloud.contract.verifier.spec.pact.PactContractConverter",
            "${project.rootDir}/build/pacts", "${project.rootDir}/src/test/resources/contracts")
}

test.dependsOn("convertContracts")

13. 如何使用传递依赖项?

Spring Cloud Contract 插件添加了为您创建存根 jar 的任务。一 出现的问题是,在重用存根时,您可能会错误地导入所有 该存根的依赖项。在构建 Maven 工件时,即使您有几个 不同的 jar 中,它们都共享一个pom.xml文件,如下面的清单所示:spring-doc.cadn.net.cn

├── producer-0.0.1.BUILD-20160903.075506-1-stubs.jar
├── producer-0.0.1.BUILD-20160903.075506-1-stubs.jar.sha1
├── producer-0.0.1.BUILD-20160903.075655-2-stubs.jar
├── producer-0.0.1.BUILD-20160903.075655-2-stubs.jar.sha1
├── producer-0.0.1.BUILD-SNAPSHOT.jar
├── producer-0.0.1.BUILD-SNAPSHOT.pom
├── producer-0.0.1.BUILD-SNAPSHOT-stubs.jar
├── ...
└── ...

有三种可能性可以使用这些依赖项,以免有任何依赖项 传递依赖项的问题:spring-doc.cadn.net.cn

13.1. 如何将所有应用程序依赖项标记为可选?

如果,在producerapplication 中,您将所有依赖项标记为可选, 当您包含producerstubs 中的存根(或当 依赖项由 Stub Runner 下载),那么,由于所有依赖项都是 可选,则不会下载它们。spring-doc.cadn.net.cn

13.2. 如何创建单独的artifactid对于 Stubs?

如果您创建单独的artifactid,您可以按所需的任何方式进行设置。 例如,您可能决定完全没有依赖项。spring-doc.cadn.net.cn

13.3. 如何排除 Consumer 端的依赖项?

作为使用者,如果您将 stub 依赖项添加到 Classpath 中,则可以显式排除不需要的依赖项。spring-doc.cadn.net.cn

14. 如何从 Contract 生成 Spring REST Docs 片段?

当您希望使用 Spring REST Docs 包含 API 的请求和响应时, 如果您使用的是 MockMvc 和 RestAssuredMockMvc,则只需对设置进行一些细微的更改。 为此,请包括以下依赖项(如果尚未这样做):spring-doc.cadn.net.cn

maven
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-contract-verifier</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.restdocs</groupId>
    <artifactId>spring-restdocs-mockmvc</artifactId>
    <optional>true</optional>
</dependency>
Gradle
testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'

接下来,您需要对基类进行一些更改。以下示例使用WebAppContext以及 RestAssure 的独立选项:spring-doc.cadn.net.cn

WebAppContext
package com.example.fraud;

import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.ExtendWith;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.restdocs.RestDocumentationContextProvider;
import org.springframework.restdocs.RestDocumentationExtension;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;

@ExtendWith(RestDocumentationExtension.class)
@SpringBootTest(classes = Application.class)
public abstract class FraudBaseWithWebAppSetup {

    @Autowired
    private WebApplicationContext context;

    @BeforeEach
    public void setup(TestInfo info, RestDocumentationContextProvider restDocumentation) {
        RestAssuredMockMvc.mockMvc(MockMvcBuilders.webAppContextSetup(this.context)
                .apply(documentationConfiguration(restDocumentation))
                .alwaysDo(document(
                        getClass().getSimpleName() + "_" + info.getDisplayName()))
                .build());
    }

    protected void assertThatRejectionReasonIsNull(Object rejectionReason) {
        assert rejectionReason == null;
    }

}
Standalone
package com.example.fraud;

import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.ExtendWith;

import org.springframework.restdocs.RestDocumentationContextProvider;
import org.springframework.restdocs.RestDocumentationExtension;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;

@ExtendWith(RestDocumentationExtension.class)
public abstract class FraudBaseWithStandaloneSetup {

    @BeforeEach
    public void setup(TestInfo info, RestDocumentationContextProvider restDocumentation) {
        RestAssuredMockMvc.standaloneSetup(MockMvcBuilders
                .standaloneSetup(new FraudDetectionController())
                .apply(documentationConfiguration(restDocumentation))
                .alwaysDo(document(
                        getClass().getSimpleName() + "_" + info.getDisplayName())));
    }

}
You need not specify the output directory for the generated snippets (since version 1.2.0.RELEASE of Spring REST Docs).

15. How Can I Use Stubs from a Location

If you want to fetch contracts or stubs from a given location without cloning a repository or fetching a JAR, use the stubs:// protocol when providing the repository root argument for Stub Runner or the Spring Cloud Contract plugin. You can read more about this in this section of the documentation.spring-doc.cadn.net.cn

16. How Can I Generate Stubs at Runtime

If you want to generate stubs at runtime for contracts, switch the generateStubs property in the @AutoConfigureStubRunner annotation, or call the withGenerateStubs(true) method on the JUnit Rule or Extension. You can read more about this in this section of the documentation.spring-doc.cadn.net.cn

17. How Can I Make The Build Pass if There Are No Contracts or Stubs

If you want Stub Runner not to fail if no stubs were found, switch the generateStubs property in the @AutoConfigureStubRunner annotation or call the withFailOnNoStubs(false) method on the JUnit Rule or Extension. You can read more about this in this section of the documentation.spring-doc.cadn.net.cn

If you want the plugins not to fail the build when no contracts were found, you can set the failOnNoStubs flag in Maven or call the contractRepository { failOnNoStubs(false) } closure in Gradle.spring-doc.cadn.net.cn

18. How Can I Mark that a Contract Is in Progress

If a contract is in progress, it means that the, on the producer side, tests are not generated, but the stub is generated. You can read more about this in this section of the documentation.spring-doc.cadn.net.cn

In a CI build, before going to production, you would like to ensure that no in-progress contracts are on the classpath, because they may lead to false positives. For this reason, by default, in the Spring Cloud Contract plugin, we set the value of failOnInProgress to true. If you want to allow such contracts when tests are to be generated, set the flag to false.spring-doc.cadn.net.cn