英文原文:https://spring.io/projects/spring-cloud-contract
目录
概述
特性
Spring Boot配置
Server / Producer方面
Client / Consumer方面
快速开始
学习
文档
示例
Spring Cloud Contract是一个总体项目,其中包含帮助用户成功实施消费者驱动合同方法的解决方案。目前,Spring Cloud Contract包含Spring Cloud Contract Verifier项目。
Spring Cloud Contract Verifier是一个支持基于JVM的应用程序的消费者驱动合同(CDC)开发的工具。它附带了用Groovy或YAML编写的合同定义语言(DSL)。合同定义用于生成以下资源:
Spring Cloud Contract Verifier将TDD提升到软件架构的水平。
要了解Spring Cloud Contract如何支持其他语言,请查看此博客文章。
在尝试测试与其他服务通信的应用程序时,我们可以执行以下两项操作之一:
两者都有其优点,但也有很多缺点。让我们关注后者。
部署所有微服务并执行端到端测试
好处:
缺点:
在单元/集成测试中模拟其他微服务
好处:
缺点:
为了解决上述问题,创建了带有Stub Runner的Spring Cloud Contract Verifier。他们的主要想法是给你非常快速的反馈,而不需要建立整个微服务世界。
Spring Cloud Contract Verifier功能:
有关详细的分步指南,请查看文档。 您可以在下面找到简化版本。
在服务器(HTTP)/生产者(消息传递)方面添加Spring Cloud Contract Verifier Maven / Gradle插件。 我们假设我们项目的组ID是com.example,工件id是http-server。
在配置中传递的测试的基类可能看起来像这样(我们假设我们正在使用[Rest Assured](http://rest-assured.io/)并且我们想要为FraudDetectionController执行CDC 你在写)
package com.example;
// imports
public class MvcTest {
@Before
public void setup() {
RestAssuredMockMvc.standaloneSetup(new FraudDetectionController());
}
}
在src/test/resources/contracts中添加一个Contract定义。 例如名为shouldMarkClientAsFraud.groovy
org.springframework.cloud.contract.spec.Contract.make {
request {
method 'PUT'
url '/fraudcheck'
body("""
{
"clientId":"1234567890",
"loanAmount":99999
}
""")
headers {
header('Content-Type', 'application/vnd.fraud.v1+json')
}
}
response {
status 200
body("""
{
"fraudCheckStatus": "FRAUD",
"rejectionReason": "Amount too high"
}
""")
headers {
header('Content-Type': 'application/vnd.fraud.v1+json')
}
}
}
如果你必须添加一个插件,将为你生成测试和存根。
Maven
org.springframework.cloud
spring-cloud-contract-maven-plugin
${spring-cloud-contract.version}
true
com.example.MvcTest
Gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootPluginVersion}"
classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:${springCloudContractVersion}"
}
}
apply plugin: 'spring-boot'
apply plugin: 'spring-cloud-contract'
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${springCloudDependencies}"
}
}
dependencies {
testCompile "org.springframework.cloud:spring-cloud-starter-contract-verifier"
}
contracts {
baseClassForTests = 'com.example.MvcTest'
}
一旦您尝试从合同构建应用程序,将在/generated-test-sources/contracts下的输出文件夹中生成测试。
package org.springframework.cloud.contract.verifier.tests;
// imports
public class ContractVerifierTest extends MvcTest {
@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")).isEqualTo("application/vnd.fraud.v1+json");
// and:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field("fraudCheckStatus").isEqualTo("FRAUD");
assertThatJson(parsedJson).field("rejectionReason").isEqualTo("Amount too high");
}
}
一旦您通过并重新运行工件的构建和安装,Spring Cloud Contract Verifier就会将合同转换为HTTP服务器存根定义。 目前我们正在支持WireMock。 存根将出现在stubs/mappings/下的输出文件夹中,如下所示:
{
"uuid" : "6c509a40-18f3-498c-a19c-c9f8b56957de",
"request" : {
"url" : "/fraudcheck",
"method" : "PUT",
"headers" : {
"Content-Type" : {
"equalTo" : "application/vnd.fraud.v1+json"
}
},
"bodyPatterns" : [ {
"matchesJsonPath" : "$[?(@.loanAmount == 99999)]"
}, {
"matchesJsonPath" : "$[?(@.clientId == '1234567890')]"
} ]
},
"response" : {
"status" : 200,
"body" : "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}",
"headers" : {
"Content-Type" : "application/vnd.fraud.v1+json"
}
}
}
CDC(消费者驱动合同)背后的想法是分享通信双方之间的合同。 Gradle和Maven插件通过使用存根分类器生成带有存根和合约定义的jar来帮助您实现这一点。 只需将其上传到某个中央存储库,其他人可以将其重新用于集成测试。
在客户端(HTTP)/使用者(消息传递)方面,它足以为正确的Spring Cloud Contract Stub Runner实现提供依赖性。 在这种情况下,因为我们的示例包含与WireMock的HTTP通信作为HTTP Server Stub。 我们将选择以下依赖项:
Maven
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud-dependencies.version}
pom
import
org.springframework.cloud
spring-cloud-starter-contract-stub-runner
test
Gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootPluginVersion}"
}
}
apply plugin: 'spring-boot'
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${springCloudDependencies}"
}
}
dependencies {
testCompile "org.springframework.cloud:spring-cloud-starter-contract-stub-runner"
}
最后一步是在测试中设置Stub Runner以自动下载所需的存根。 要实现这一点,您必须传递@AutoConfigureStubRunner注释。 该注释具有一组您可以设置的属性。 如果您不喜欢这种方法,您也可以在测试属性中设置这些值。
对于Spring Cloud Contract 1.2.x
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureStubRunner(ids = {"com.example:http-server:+:stubs:8080"}, workOffline = true)
public class LoanApplicationServiceTests {
对于从2.0.x开始的Spring Cloud Contract
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureStubRunner(ids = {"com.example:http-server:+:stubs:8080"}, stubsMode = StubRunnerProperties.StubsMode.LOCAL)
public class LoanApplicationServiceTests {
这样,具有组ID com.example的工件,工件id http-server,在最新版本中,具有存根分类器将在端口8080处注册。由于传递了workOffline标志,因此将不从远程存储库下载存根 - 它 将在当地的Maven回购中搜索。 一旦您的测试上下文启动,执行以下代码将不会导致404,因为Spring Cloud Contract Stub Runner将自动启动测试中的WireMock服务器并使用从服务器端生成的存根来提供它。
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Content-Type", "application/vnd.fraud.v1+json");
String response = restTemplate.exchange("http://localhost:8080/fraudcheck", HttpMethod.PUT,
new HttpEntity<>("{\"clientId\":\"1234567890\",\"loanAmount\":99999}", httpHeaders),
String.class);
assertThat(response).isEqualTo("{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}");
使用Spring Initializr引导您的应用程序。
每个Spring项目都有自己的; 它详细解释了如何使用项目功能以及使用它们可以实现的功能。
2.1.0 RC3 PRE CURRENT | Reference Doc. | API Doc. |
2.0.3 SNAPSHOT CURRENT | Reference Doc. | API Doc. |
2.0.2 CURRENT GA | Reference Doc. | API Doc. |
1.2.6 SNAPSHOT CURRENT | Reference Doc. | API Doc. |
1.2.5 CURRENT GA | Reference Doc. | API Doc. |
1.1.6 SNAPSHOT | Reference Doc. | API Doc. |
1.1.5 GA | Reference Doc. | API Doc. |
1.0.5 SNAPSHOT | Reference Doc. | API Doc. |
1.0.4 GA | Reference Doc. | API Doc. |
尝试一些例子: