“作方法”指南
1. 为什么要使用 Spring Cloud Contract?
Spring Cloud Contract 在多语言环境中运行良好。这个项目有很多非常有趣的功能。其中相当多的功能绝对使Spring Cloud Contract Verifier 在消费者驱动合约市场上脱颖而出(CDC) 工具。最有趣的功能包括:
-
能够通过消息传递进行 CDC。
-
清晰易用的静态类型 DSL。
-
能够将当前 JSON 文件复制粘贴到合约中,并且仅编辑其元素。
-
从定义的合约自动生成测试。
-
存根运行器功能:存根在运行时自动从 Nexus/Artifactory 下载。
-
Spring Cloud 集成:集成测试不需要发现服务。
-
Spring Cloud Contract 与 Pact 集成,并提供了简单的钩子来扩展其功能。
-
能够通过 Docker 添加对任何语言和框架的支持。
2. 如何用 Groovy 以外的语言编写合约?
你可以用 YAML 编写合约。有关详细信息,请参阅此部分。
我们正在努力允许更多描述合约的方式。您可以查看 github-issues 了解更多信息。
3. 如何为合约提供动态值?
与存根相关的最大挑战之一是它们的可重用性。只有当它们能够被广泛使用时,它们才能达到其目的。请求和响应元素的硬编码值(例如日期和 ID)通常使这变得困难。考虑以下 JSON 请求:
{
"time" : "2016-10-10 20:10:15",
"id" : "9febab1c-6f36-4a0b-88d6-3b6a6d81cd4a",
"body" : "foo"
}
现在考虑以下 JSON 响应:
{
"time" : "2016-10-10 21:10:15",
"id" : "c4231e1f-3ca9-48d3-b7e7-567d55f0d051",
"body" : "bar"
}
想象一下设置适当的值所需的痛苦time
字段(假设此内容是由数据库生成的),方法是更改系统中的时钟或提供数据提供者的存根实现。同样是相关的到名为id
. 您可以创建 UUID 生成器的存根实现,但这样做没有什么意义。
因此,作为消费者,您希望发送与任何形式的时间或任何 UUID 匹配的请求。这样,您的系统可以照常工作,无需存根即可生成数据。假设,在上述JSON 的情况下,最重要的部分是body
田。 您可以专注于这一点并为其他字段提供匹配。换句话说,您希望存根按如下方式工作:
{
"time" : "SOMETHING THAT MATCHES TIME",
"id" : "SOMETHING THAT MATCHES UUID",
"body" : "foo"
}
就响应而言,作为消费者,您需要一个可以作的具体值。因此,以下 JSON 是有效的:
{
"time" : "2016-10-10 21:10:15",
"id" : "c4231e1f-3ca9-48d3-b7e7-567d55f0d051",
"body" : "bar"
}
在前面的章节中,我们从合约中生成了测试。因此,从生产者方面来看,情况看起来大不相同。我们解析提供的合约,在测试中,我们希望向您的端点发送一个真实的请求。因此,对于请求的生产者,我们不能进行任何形式的匹配。我们需要具体的值,以便生产者的后端可以工作。因此,以下 JSON 将是有效的:
{
"time" : "2016-10-10 20:10:15",
"id" : "9febab1c-6f36-4a0b-88d6-3b6a6d81cd4a",
"body" : "foo"
}
另一方面,从合同有效性的角度来看,响应不一定必须包含具体值time
或id
. 假设您在生产者端生成这些。同样,您必须进行大量存根处理,以确保始终返回相同的值。这就是为什么从生产者您可能需要以下响应:
{
"time" : "SOMETHING THAT MATCHES TIME",
"id" : "SOMETHING THAT MATCHES UUID",
"body" : "bar"
}
然后,您如何为消费者提供匹配器,为生产者提供具体值(在其他时间则相反)?Spring Cloud Contract 允许您提供动态值。这意味着它对于双方来说可能不同沟通的双方。
您可以在合约 DSL 部分阅读有关此内容的更多信息。
阅读与 JSON 相关的 Groovy 文档,了解如何正确构建请求和响应正文。 |
4. 如何进行存根版本控制?
本节介绍存根的版本,您可以通过多种不同的方式处理这些版本:
4.1. API 版本控制
版本控制的真正含义是什么?如果参考 API 版本,有 不同的方法:
-
使用超媒体链接,并且无论如何都不要对您的 API 进行版本控制
-
通过标头和 URL 传递版本
我们不会试图回答哪种方法更好的问题。你应该选择任何适合您的需求并让您产生商业价值。
假设您确实对 API 进行了版本控制。在这种情况下,您应该提供尽可能多的合约和您支持的版本。您可以为每个版本创建一个子文件夹,或将其附加到合约名称中 - 任何最适合您的版本。
4.2. JAR版本控制
如果版本控制是指包含存根的 JAR 版本,那么基本上有两种主要方法。
假设您执行持续交付和部署,这意味着您每次通过管道时都会生成一个新版本的jar,并且jar可以随时投入生产。例如,您的jar版本如下所示(因为它是在2016年10月20日20:15:21上构建的):
1.0.0.20161020-201521-RELEASE
在这种情况下,您生成的存根 jar 应该如下所示:
1.0.0.20161020-201521-RELEASE-stubs.jar
在这种情况下,您应该在application.yml
或@AutoConfigureStubRunner
什么时候 引用存根时,提供存根的最新版本。您可以通过传递符号来做到这一点。以下示例显示了如何执行此作:+
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:8080"})
但是,如果版本控制是固定的(例如,1.0.4.RELEASE
或2.1.1
),您必须设置 jar 的具体值 版本。 以下示例显示了如何为版本 2.1.1 执行此作:
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:2.1.1:stubs:8080"})
4.3. 开发或生产存根
您可以作分类器来针对当前开发版本运行测试其他服务的存根或已部署到生产环境的存根。如果您更改您的构建以使用prod-stubs
分类器一旦你到达生产部署,你可以在一个使用开发存根的情况下运行测试,在一个使用生产存根的情况下运行测试。
以下示例适用于使用存根开发版本的测试:
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:8080"})
以下示例适用于使用存根生产版本的测试:
@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:prod-stubs:8080"})
还可以在部署管道的属性中传递这些值。
5. 如何将通用存储库与合约一起使用,而不是将它们存储在生产者那里?
存储合同的另一种方式,而不是将它们交给生产者,是保留 它们在一个共同的地方。这种情况可能与安全问题有关(其中 消费者无法克隆生产者的代码)。此外,如果您将合同保存在一个地方, 然后,作为生产者,你知道你有多少消费者,你可能会破坏哪个消费者 与您本地的更改。
5.1. Repo 结构
假设我们有一个坐标为com.example:server
和三个
消费者:client1
,client2
和client3
.然后,在存储库中,使用 common
合约,您可以进行以下设置(您可以在此处查看)。
以下列表显示了这样的结构:
├── 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
,client2
和client3
).期望是标准的 Groovy DSL
contract 文件,如本文档中所述。此存储库必须生成一个 JAR 文件,该文件映射
一对一访问存储库的内容。
以下示例显示了pom.xml
在server
文件夹:
<?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.3.10.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<spring-cloud-contract.version>2.2.8.BUILD-SNAPSHOT</spring-cloud-contract.version>
<spring-cloud-release.version>Hoxton.BUILD-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 文件是消费者端运行所必需的mvn clean install -DskipTests
在本地安装
生产者项目的存根。
这pom.xml
可以如下所示:
<?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。以下示例 显示这样的设置:
<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 是在消费者和 制片人方面。在公共存储库中也有适当的插件设置,其中 合同。CI 作业设置为公共存储库,以构建所有 合同并将其上传到 Nexus/Artifactory。下图显示了此 UML 工作流程:

5.3. 消费者
当消费者想要离线处理合约而不是克隆生产者时
代码,消费者团队克隆公共存储库,转到所需生产者的
文件夹(例如,com/example/server
) 并运行mvn clean install -DskipTests
自
在本地安装从合约转换的存根。
您需要在本地安装 Maven |
5.4. 生产者
作为生产者,您可以更改 Spring Cloud Contract Verifier 以提供 URL 和 包含合约的 JAR 的依赖关系,如下所示:
<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>
通过此设置,groupid 为com.example.standalone
和 artifactidcontracts
下载自link/to/your/nexus/or/artifactory/or/sth
.是的
然后在本地临时文件夹中解压,并且com/example/server
被选为用于生成测试和存根的存根。由于
对于这个约定,生产者团队可以知道哪些消费者团队会在什么时候被破坏
进行了一些不兼容的更改。
其余的流程看起来是一样的。
5.5. 如何定义每个主题而不是每个生产者的消息传递契约?
为了避免在公共存储库中重复消息传递契约,当几个生产者将消息写入一个主题时,我们可以创建一个结构,其中 REST 契约被放置在每个生产者的文件夹中,而消息传递契约被放置在每个主题的文件夹中。
5.5.1. 对于 Maven 项目
为了使在生产者端工作成为可能,我们应该为通过我们感兴趣的消息传递主题过滤常见的存储库 jar 文件。 这includedFiles
Maven Spring Cloud Contract 插件的属性让我们这样做。 也contractsPath
需要指定,因为默认路径为公共存储库groupid/artifactid
. 以下示例显示了 MavenSpring Cloud Contract 的插件:
<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 项目,请执行以下作:
-
为公共存储库依赖项添加自定义配置,如下所示:
ext { contractsGroupId = "com.example" contractsArtifactId = "common-repo" contractsVersion = "1.2.3" } configurations { contracts { transitive = false } }
-
将公共存储库依赖项添加到类路径,如下所示:
dependencies { contracts "${contractsGroupId}:${contractsArtifactId}:${contractsVersion}" testCompile "${contractsGroupId}:${contractsArtifactId}:${contractsVersion}" }
-
将依赖项下载到适当的文件夹,如下所示:
task getContracts(type: Copy) { from configurations.contracts into new File(project.buildDir, "downloadedContracts") }
-
解压缩 JAR,如下所示:
task unzipContracts(type: Copy) { def zipFile = new File(project.buildDir, "downloadedContracts/${contractsArtifactId}-${contractsVersion}.jar") def outputDir = file("${buildDir}/unpackedContracts") from zipTree(zipFile) into outputDir }
-
清理未使用的合约,如下所示:
task deleteUnwantedContracts(type: Delete) { delete fileTree(dir: "${buildDir}/unpackedContracts", include: "**/*", excludes: [ "**/${project.name}/**"", "**/${first-topic}/**", "**/${second-topic}/**"]) }
-
创建任务依赖项,如下所示:
unzipContracts.dependsOn("getContracts") deleteUnwantedContracts.dependsOn("unzipContracts") build.dependsOn("deleteUnwantedContracts")
-
通过指定包含合约的目录来配置插件,方法是将 这
contractsDslDir
属性,如下所示:contracts { contractsDslDir = new File("${buildDir}/unpackedContracts") }
6. 如何使用 Git 作为合约和存根的存储?
在多语言世界中,有些语言不使用二进制存储,例如Artifactory 或 Nexus 使用。从 Spring Cloud Contract 2.0.0 版本开始,我们提供了机制将合约和存根存储在 SCM(源代码控制管理)存储库中。目前,唯一支持的 SCM 是 Git。
存储库必须具有以下设置(您可以从此处签出):
.
└── 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
文件夹:
-
我们按以下方式对应用程序进行分组
groupId
(例如com.example
). -
每个应用程序都由其
artifactId
(例如,beer-api-producer-git
). -
接下来,每个应用程序按其版本组织(例如
0.0.1-SNAPSHOT
).开始 从 Spring Cloud Contract 版本2.1.0
,您可以按如下方式指定版本 (假设您的版本遵循语义版本控制):-
+
或latest
:查找存根的最新版本(假设快照 始终是给定修订号的最新工件)。这意味着:-
如果您有
1.0.0.RELEASE
,2.0.0.BUILD-SNAPSHOT
和2.0.0.RELEASE
,我们假设 最新的是2.0.0.BUILD-SNAPSHOT
. -
如果您有
1.0.0.RELEASE
和2.0.0.RELEASE
,我们假设最新的是2.0.0.RELEASE
. -
如果你有一个名为
latest
或者,我们将选择该文件夹。+
-
-
release
:查找存根的最新版本。这意味着:-
如果您有
1.0.0.RELEASE
,2.0.0.BUILD-SNAPSHOT
和2.0.0.RELEASE
我们假设 最新的是2.0.0.RELEASE
. -
如果你有一个名为
release
,我们选择该文件夹。
-
-
最后,有两个文件夹:
-
contracts
:好的做法是存储每个人所需的合同 consumer 在具有消费者名称的文件夹中(例如beer-api-consumer
).这样,您 可以使用stubs-per-consumer
特征。进一步的目录结构是任意的。 -
mappings
:Maven 或 Gradle Spring Cloud Contract 插件推送 此文件夹中的存根服务器映射。在消费者端,Stub Runner 扫描此文件夹 以启动具有存根定义的存根服务器。文件夹结构是副本 在contracts
子文件夹。
6.1. 协议公约
控制合同来源的类型和位置(是否 二进制存储或 SCM 存储库),您可以在 存储库。Spring Cloud Contract 迭代已注册的协议解析器 并尝试获取合约(通过使用插件)或存根(从 Stub Runner)。
对于 SCM 功能,目前我们支持 Git 存储库。要使用它,
在需要放置存储库 URL 的属性中,您必须添加前缀
连接 URL 与git://
.以下列表显示了一些示例:
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://
协议。
您必须手动添加pushStubsToScm 目标或执行(绑定)Maven 中的pushStubsToScm task 中的
Gradle。我们不会将存根推送到origin 你的 git
存储 库。 |
以下列表包括 Maven 和 Gradle 构建文件的相关部分:
<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>
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 executed
*/
publish.dependsOn("publishStubsToScm")
还可以进一步定制publishStubsToScm
gradle 任务。在以下示例中,该任务被自定义为从本地 Git 存储库中选择合约:
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.RELEASE
这customize{}
闭包之前用于publishStubsToScm
自定义不再可用。设置应直接应用在publishStubsToScm
闭包,如上例所示。
使用这样的设置:
-
将 git 项目克隆到临时目录
-
SCM 存根下载器转到
META-INF/groupId/artifactId/version/contracts
文件夹 以查找合同。例如,对于com.example:foo:1.0.0
,路径将是META-INF/com.example/foo/1.0.0/contracts
. -
测试是从合约生成的。
-
存根是根据合同创建的。
-
测试通过后,存根将提交到克隆的存储库中。
-
最后,将推送发送到该存储库的
origin
.
6.3. 合约存储在本地的生产者
使用 SCM 作为存根和合约目标的另一种选择是将合约存储在本地,与生产者一起,并且仅将合约和存根推送到 SCM。以下列表显示了使用 Maven 和 Gradle 实现此目的所需的设置:
<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>
contracts {
// 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 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/"
}
// The mode can't be classpath
contractsMode = "LOCAL"
}
}
publish.dependsOn("publishStubsToScm")
publishToMavenLocal.dependsOn("publishStubsToScm")
使用这样的设置:
-
默认合同
src/test/resources/contracts
目录被选中。 -
测试是从合约生成的。
-
存根是根据合同创建的。
-
测试通过后:
-
git 项目被克隆到临时目录。
-
存根和协定在克隆的存储库中提交。
-
-
最后,将推送到该存储库的
origin
.
6.4. 将与生产者的契约和存根保存在外部存储库中
您也可以将合约保留在生产者存储库中,但将存根保留在外部 git 存储库中。 当您想要使用基本使用者-生产者协作流但不能使用时,这最有用 使用工件存储库来存储存根。
为此,请使用通常的生产者设置,然后将pushStubsToScm
目标和设定contractsRepositoryUrl
到要保存存根的存储库。
6.5. 消费者
在消费者端,当传递repositoryRoot
参数
从@AutoConfigureStubRunner
注释,则
JUnit 规则、JUnit 5 扩展或属性,您可以传递
SCM 存储库,前缀为git://
协议。 以下示例显示了如何执行此作:
@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"
)
使用这样的设置:
-
git 项目被克隆到临时目录。
-
SCM 存根下载器转到
META-INF/groupId/artifactId/version/
文件夹 以查找存根定义和协定。例如,对于com.example:foo:1.0.0
,路径将是META-INF/com.example/foo/1.0.0/
. -
存根服务器启动并输入映射。
-
在消息传递测试中读取和使用消息传递定义。
7. 如何使用 Pact 经纪人?
使用 Pact 时,您可以使用 Pact Broker 来存储和共享 Pact 定义。从 Spring Cloud Contract2.0.0 开始,您可以从 Pact Broker 获取 Pact 文件以生成测试和存根。
Pact 遵循消费者合同惯例。这意味着 消费者首先创建契约定义,然后再创建 与制作人共享文件。这些期望是产生的 如果期望 没有得到满足。 |
7.1. 如何使用 Pact
Spring Cloud Contract 包括对 Pact 表示的支持
合同直到版本 4。您可以使用 Pact 文件来代替 DSL。在本节中,我们将
展示如何为您的项目添加 Pact 支持。但请注意,并非所有功能都受支持。
从版本 3 开始,您可以为同一元素组合多个匹配器;
您可以对正文、标头、请求和路径使用匹配器;您可以使用价值生成器。
Spring Cloud Contract 目前仅支持使用AND
规则逻辑。旁边,在转换过程中将跳过请求和路径匹配器。使用具有给定格式的日期、时间或日期时间值生成器时,跳过给定格式并使用 ISO 格式。
7.2. 契约转换器
为了正确支持 Spring Cloud Contract 进行消息传递的方式使用 Pact,您必须提供一些额外的元数据条目。
要定义消息发送到的目的地,您必须将metaData
条目与sentTo
等于要发送消息的目标的键(例如,"metaData": { "sentTo": "activemq:output" }
).
7.3. 契约合同
Spring Cloud Contract 可以读取 Pact JSON 定义。您可以将文件放在src/test/resources/contracts
文件夹。 记得把spring-cloud-contract-pact
依赖于你的类路径。以下示例显示了这样的 Pact 契约:
{
"provider": {
"name": "Provider"
},
"consumer": {
"name": "Consumer"
},
"interactions": [
{
"description": "",
"request": {
"method": "PUT",
"path": "/pactfraudcheck",
"headers": {
"Content-Type": "application/json"
},
"body": {
"clientId": "1234567890",
"loanAmount": 99999
},
"generators": {
"body": {
"$.clientId": {
"type": "Regex",
"regex": "[0-9]{10}"
}
}
},
"matchingRules": {
"header": {
"Content-Type": {
"matchers": [
{
"match": "regex",
"regex": "application/json.*"
}
],
"combine": "AND"
}
},
"body": {
"$.clientId": {
"matchers": [
{
"match": "regex",
"regex": "[0-9]{10}"
}
],
"combine": "AND"
}
}
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"fraudCheckStatus": "FRAUD",
"rejection.reason": "Amount too high"
},
"matchingRules": {
"header": {
"Content-Type": {
"matchers": [
{
"match": "regex",
"regex": "application/json.*"
}
],
"combine": "AND"
}
},
"body": {
"$.fraudCheckStatus": {
"matchers": [
{
"match": "regex",
"regex": "FRAUD"
}
],
"combine": "AND"
}
}
}
}
}
],
"metadata": {
"pact-specification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.5.13"
}
}
}
7.4. 生产者契约
在生产者端,您必须向插件添加两个额外的依赖项 配置。 一个是 Spring Cloud Contract Pact 支持,另一个表示您使用的当前 Pact 版本。以下列表显示了如何对Maven 和 Gradle 执行此作:
// if additional dependencies are needed e.g. for Pact
classpath "org.springframework.cloud:spring-cloud-contract-pact:${findProperty('verifierVersion') ?: verifierVersion}"
执行应用程序的构建时,将生成一个测试和存根。以下内容 示例显示了来自此过程的测试和存根:
@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:
7.6. 与 Pact Broker 沟通
每当repositoryRoot
属性以 Pact 协议开头
(开头为pact://
),存根下载器尝试
从 Pact Broker 获取 Pact 合约定义。
无论设置什么pact://
被解析为 Pact 代理 URL。
通过设置环境变量、系统属性或属性集 在插件或合约存储库配置中,您可以 调整下载器的行为。下表描述了 性能:
属性名称 |
默认值 |
描述 |
* * * |
将 URL 中的主机传递给 |
协议代理的 URL。 |
* * * |
从 URL 传递给 |
Pact Broker 的端口。 |
* * * |
从 URL 传递到 |
Pact Broker 的协议。 |
* * * |
存根的版本,或 |
应用于获取存根的标签。 |
* * * |
|
应用于连接到 Pact 代理的身份验证类型。 |
* * * |
传递给的用户名 |
连接到 Pact 代理时要使用的用户名。 |
* * * |
传递给 |
连接到 Pact 代理时使用的密码。 |
* * * |
false |
什么时候 |
7.7. Flow:使用 Pact Broker 的消费者合约方法 |消费者方面
消费者使用 Pact 框架生成 Pact 文件。这 Pact 文件将发送到 Pact Broker。您可以在此处找到此类设置的示例。
7.8. 流程:生产者端与 Pact Broker 的消费者合同方法
为了让生产者使用 Pact Broker 中的 Pact 文件,我们可以重用
与我们用于外部合同的机制相同。我们路由 Spring Cloud Contract
将 Pact 实现与包含
这pact://
协议。您可以将 URL 传递给
契约经纪人。您可以在此处找到此类设置的示例。
以下列表显示了 Maven 和 Gradle 的配置详细信息:
<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>
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.
}
使用这样的设置:
-
Pact 文件是从 Pact 代理下载的。
-
Spring Cloud Contract 将 Pact 文件转换为测试和存根。
-
像往常一样,将自动创建带有存根的 JAR。
7.9. Flow:在消费者端与 Pact 的生产者合同方法
在不想执行消费者契约方法的场景中 (对于每个消费者,定义期望)但您更喜欢 执行生产者合同(生产者提供合同和 publishes 存根),您可以将 Spring Cloud Contract 与 存根流道选项。您可以在此处找到此类设置的示例。
记得添加 Stub Runner 和 Spring Cloud Contract Pact 模块 作为测试依赖项。
以下列表显示了 Maven 和 Gradle 的配置详细信息:
<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>
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
),如下所示
示例显示:
@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;
//...
}
使用这样的设置:
-
Pact 文件是从 Pact 代理下载的。
-
Spring Cloud Contract 将 Pact 文件转换为存根定义。
-
存根服务器被启动并输入存根。
8. 如何调试生成的测试客户端发送的请求/响应?
生成的测试都以某种形式或方式归结为 RestAssured。放心 依赖于 Apache HttpClient。 HttpClient 有一个称为 wire logging 的工具, 它将整个请求和响应记录到 HttpClient。Spring Boot 有一个日志记录通用应用程序属性来执行此类作。若要使用它,请将其添加到应用程序属性中,如下所示:
logging.level.org.apache.http.wire=DEBUG
9. 如何调试 WireMock 发送的映射、请求或响应?
从版本开始1.2.0
,我们打开 WireMock 日志记录info
并将 WireMock 通知程序设置为详细。现在你可以
确切地知道 WireMock 服务器收到了什么请求以及哪些请求
选择匹配的响应定义。
要关闭此功能,请将 WireMock 日志记录设置为ERROR
如下:
logging.level.com.github.tomakehurst.wiremock=ERROR
10. 如何查看 HTTP 服务器存根中注册的内容?
您可以使用mappingsOutputFolder
属性@AutoConfigureStubRunner
,StubRunnerRule
或
'StubRunnerExtension'转储每个项目 ID 的所有映射。还有给定存根服务器所在的端口
已启动并附上。
11. 如何从文件中引用文本?
在 1.2.0 版本中,我们添加了此功能。您可以调用file(…)
方法
DSL 并提供相对于合约所在位置的路径。
如果您使用 YAML,则可以使用bodyFromFile
财产。
12. 如何从 Spring Cloud 合约合约生成 Pact、YAML 或 X 文件?
Spring Cloud Contract 附带了一个ToFileContractsTransformer
允许您转储的类
合同作为给定文件ContractConverter
.它包含一个static void main
方法,允许您将转换器作为可执行文件执行。它需要以下内容
参数:
-
论点 1 :
FQN
:完全限定的名称ContractConverter
(例如,PactContractConverter
).必需。 -
论点 2 :
path
:应存储转储文件的路径。OPTIONAL — 默认为target/converted-contracts
. -
论点 3 :
path
:应搜索合同的路径。OPTIONAL — 默认为src/test/resources/contracts
.
执行 transformer 后,处理 Spring Cloud Contract 文件,并且,
根据提供的 FQNContractTransformer
,合约被转换
转换为所需的格式并转储到提供的文件夹。
以下示例展示了如何为 Maven 和 Gradle 配置 Pact 集成:
<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>
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 工件时,即使您有几个 在不同的罐子中,它们都共享一个 POM,如以下列表所示:
├── 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
├── ...
└── ...
有三种可能使用这些依赖项,以免有任何依赖项 传递依赖关系的问题:
-
将所有应用程序依赖项标记为可选
-
为存根创建单独的项目
-
排除消费者端的依赖关系
13.1. 如何将所有应用程序依赖项标记为可选?
如果,在producer
application,则将所有依赖项标记为可选,
当您将producer
存根(或者当
依赖项由存根运行器下载),因为所有依赖项都是
可选,则不会下载它们。
13.2. 如何创建单独的artifactid
对于存根?
如果您创建单独的artifactid
,您可以按照任何您想要的方式进行设置。
例如,您可能决定根本没有依赖项。
13.3. 如何排除消费者端的依赖关系?
作为消费者,如果您将存根依赖项添加到类路径中,则可以显式排除不需要的依赖项。
14. 如何从合约中生成 Spring REST 文档片段?
当你想使用 Spring REST Docs 包含 API 的请求和响应时, 如果您使用的是 MockMvc 和 RestAssuredMockMvc,则只需对设置进行一些细微的更改。 为此,请包含以下依赖项(如果您尚未这样做):
<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>
testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
接下来,您需要对基类进行一些更改。以下示例使用WebAppContext
以及带有 RestAssured 的独立选项:
package com.example.fraud;
import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.test.context.junit4.SpringRunner;
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;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public abstract class FraudBaseWithWebAppSetup {
private static final String OUTPUT = "target/generated-snippets";
@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(OUTPUT);
@Rule
public TestName testName = new TestName();
@Autowired
private WebApplicationContext context;
@Before
public void setup() {
RestAssuredMockMvc.mockMvc(MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.alwaysDo(document(
getClass().getSimpleName() + "_" + testName.getMethodName()))
.build());
}
protected void assertThatRejectionReasonIsNull(Object rejectionReason) {
assert rejectionReason == null;
}
}
package com.example.fraud;
import io.restassured.module.mockmvc.RestAssuredMockMvc;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestName;
import org.springframework.restdocs.JUnitRestDocumentation;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
public abstract class FraudBaseWithStandaloneSetup {
private static final String OUTPUT = "target/generated-snippets";
@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(OUTPUT);
@Rule
public TestName testName = new TestName();
@Before
public void setup() {
RestAssuredMockMvc.standaloneSetup(MockMvcBuilders
.standaloneSetup(new FraudDetectionController())
.apply(documentationConfiguration(this.restDocumentation))
.alwaysDo(document(
getClass().getSimpleName() + "_" + testName.getMethodName())));
}
}
您无需为生成的代码片段指定输出目录(自 Spring REST Docs 版本 1.2.0.RELEASE 开始)。 |
15. 如何使用某个位置的存根
如果您想从给定位置获取合约或存根而不克隆存储库或获取 JAR,只需使用stubs://
协议,为存根运行器或 Spring Cloud Contract 插件提供存储库根参数时。您可以在文档的这一部分中阅读有关此内容的更多信息。
16. 如何在运行时生成存根
如果你想在运行时为合约生成存根,只需将generateStubs
属性中的@AutoConfigureStubRunner
注释,或调用withGenerateStubs(true)
方法。您可以在文档的这一部分中阅读有关此内容的更多信息。
17. 如果没有合同或存根,如何通过构建
如果希望 Stub Runner 在未找到存根时不会失败,只需将generateStubs
属性中的@AutoConfigureStubRunner
注释,或调用withFailOnNoStubs(false)
方法。您可以在文档的这一部分中阅读有关此内容的更多信息。
如果您希望插件在未找到合约时不会使构建失败,您可以将failOnNoStubs
标记或调用contractRepository { failOnNoStubs(false) }
Gradle 关闭。
18. 如何标记合同正在进行中
如果合约正在进行中,则意味着不会生成生产者端的测试,但存根会生成。您可以在文档的这一部分中阅读有关此内容的更多信息。
在 CI 构建中,在进入生产环境之前,您希望确保类路径上没有正在进行的协定。那是因为您可能会导致误报。这就是为什么在默认情况下,在 Spring Cloud Contract 插件中,我们将failOnInProgress
自true
.如果要在生成测试时允许此类合约,只需将标志设置为false
.