Spring Cloud Contract 将 TDD 移至软件体系结构级别。 它允许您执行使用者驱动和生产者驱动的合同测试。
历史
在成为 Spring Cloud Contract 之前,这个项目被称为 Accurest。 它由来自 (Codearte) 的 Marcin Grzejszczak 和 Jakub Kubrynski 创建。
该版本于 2015 年 1 月 26 日发布,并于 2016 年 2 月 29 日发布后变得稳定。0.1.0
1.0.0
测试问题
如果我们想测试前面图片左上角的应用程序 部分来确定它是否可以与其他服务通信,我们可以执行 两件事:
-
部署所有微服务并执行端到端测试。
-
在单元测试和集成测试中模拟其他微服务。
两者都有其优点,但也有很多缺点。
部署所有微服务并执行端到端测试
优势:
-
模拟生产。
-
测试服务之间的真实通信。
弊:
-
要测试一个微服务,我们必须部署六个微服务、几个数据库、 和其他项目。
-
运行测试的环境被锁定为一组测试(没有其他人 将能够同时运行测试)。
-
它们需要很长时间才能运行。
-
反馈在这个过程中很晚才出现。
-
它们非常难以调试。
在单元测试和集成测试中模拟其他微服务
优势:
-
他们提供非常快速的反馈。
-
他们没有基础设施要求。
弊:
-
服务的实现者创建的存根可能与 现实。
-
您可以在通过测试但生产失败的情况下进入生产环境。
为了解决上述问题,创建了 Spring Cloud Contract。主要思想是 为您提供非常快速的反馈,而无需设置 整个微服务世界。如果您处理存根,那么您唯一需要的应用程序 是您的应用程序直接使用的值。下图显示了这种关系 of stub 添加到应用程序:

Spring Cloud Contract 可以确定您使用的存根是 由您调用的服务创建。此外,如果您可以使用它们,则意味着它们 对制片人一方进行了测试。简而言之,您可以信任这些存根。
目的
Spring Cloud Contract 的主要目的是:
-
确保 HTTP 和消息传递存根(在开发客户端时使用)完全执行 实际的服务器端实现的作用。
-
推广 ATDD (验收测试驱动开发) 方法和微服务架构风格。
-
提供一种在合同中发布更改的方法,这些更改在双方都立即可见。
-
生成要在服务器端使用的样板测试代码。
默认情况下,Spring Cloud Contract 作为 HTTP 服务器存根与 Wiremock 集成。
Spring Cloud Contract 的目的不是开始编写业务 合同中的特征。假设我们有一个欺诈检查的业务使用案例。如果 用户可能由于 100 种不同的原因而成为欺诈者,我们假设您会创建两个 合同,一个用于 positive 情况,一个用于 negative 情况。Contract 测试包括 用于测试应用程序之间的协定,而不是模拟完整行为。 |
Spring Cloud Contract 的目的不是开始编写业务 合同中的特征。假设我们有一个欺诈检查的业务使用案例。如果 用户可能由于 100 种不同的原因而成为欺诈者,我们假设您会创建两个 合同,一个用于 positive 情况,一个用于 negative 情况。Contract 测试包括 用于测试应用程序之间的协定,而不是模拟完整行为。 |
什么是合同?
作为服务的消费者,我们需要定义我们到底想要实现什么。我们需要 制定我们的期望。这就是我们编写合同的原因。换句话说,合约是 关于 API 或消息通信外观的协议。请考虑以下示例:
假设您要发送一个请求,其中包含客户公司的 ID 和
它想向我们借款的金额。您还希望使用
方法。下面的清单显示了一个合同,用于检查客户是否应该
在 Groovy 和 YAML 中都标记为欺诈:/fraudcheck
PUT
-
groovy
org.springframework.cloud.contract.spec.Contract.make {
request { // (1)
method 'PUT' // (2)
url '/fraudcheck' // (3)
body([ // (4)
"client.id": $(regex('[0-9]{10}')),
loanAmount : 99999
])
headers { // (5)
contentType('application/json')
}
}
response { // (6)
status OK() // (7)
body([ // (8)
fraudCheckStatus : "FRAUD",
"rejection.reason": "Amount too high"
])
headers { // (9)
contentType('application/json')
}
}
}
/*
From the Consumer perspective, when shooting a request in the integration test:
(1) - If the consumer sends a request
(2) - With the "PUT" method
(3) - to the URL "/fraudcheck"
(4) - with the JSON body that
* has a field `client.id` that matches a regular expression `[0-9]{10}`
* has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the response will be sent with
(7) - status equal `200`
(8) - and JSON body equal to
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` equal to `application/json`
From the Producer perspective, in the autogenerated producer-side test:
(1) - A request will be sent to the producer
(2) - With the "PUT" method
(3) - to the URL "/fraudcheck"
(4) - with the JSON body that
* has a field `client.id` that will have a generated value that matches a regular expression `[0-9]{10}`
* has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the test will assert if the response has been sent with
(7) - status equal `200`
(8) - and JSON body equal to
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` matching `application/json.*`
*/
request: # (1)
method: PUT # (2)
url: /yamlfraudcheck # (3)
body: # (4)
"client.id": 1234567890
loanAmount: 99999
headers: # (5)
Content-Type: application/json
matchers:
body:
- path: $.['client.id'] # (6)
type: by_regex
value: "[0-9]{10}"
response: # (7)
status: 200 # (8)
body: # (9)
fraudCheckStatus: "FRAUD"
"rejection.reason": "Amount too high"
headers: # (10)
Content-Type: application/json
#From the Consumer perspective, when shooting a request in the integration test:
#
#(1) - If the consumer sends a request
#(2) - With the "PUT" method
#(3) - to the URL "/yamlfraudcheck"
#(4) - with the JSON body that
# * has a field `client.id`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}`
#(7) - then the response will be sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json`
#
#From the Producer perspective, in the autogenerated producer-side test:
#
#(1) - A request will be sent to the producer
#(2) - With the "PUT" method
#(3) - to the URL "/yamlfraudcheck"
#(4) - with the JSON body that
# * has a field `client.id` `1234567890`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(7) - then the test will assert if the response has been sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json`
预计 Contract 来自受信任的来源。切勿下载来自不受信任位置的合约,也不应与之交互。 |
预计 Contract 来自受信任的来源。切勿下载来自不受信任位置的合约,也不应与之交互。 |