跳到主要内容

常用顶层元素

DeepSeek V3 中英对照 Common Top-Level Elements

以下部分描述了最常见的顶层元素:

描述​

你可以为你的合约添加一个 description。描述可以是任意文本。以下代码展示了一个示例:

org.springframework.cloud.contract.spec.Contract.make {
description('''
given:
An input
when:
Sth happens
then:
Output
''')
}
description: Some description
name: some name
priority: 8
ignored: true
request:
url: /foo
queryParameters:
a: b
b: c
method: PUT
headers:
foo: bar
fooReq: baz
body:
foo: bar
matchers:
body:
- path: $.foo
type: by_regex
value: bar
headers:
- key: foo
regex: bar
response:
status: 200
headers:
foo2: bar
foo3: foo33
fooRes: baz
body:
foo2: bar
foo3: baz
nullValue: null
matchers:
body:
- path: $.foo2
type: by_regex
value: bar
- path: $.foo3
type: by_command
value: executeMe($it)
- path: $.nullValue
type: by_null
value: null
headers:
- key: foo2
regex: bar
- key: foo3
command: andMeToo($it)
Contract.make(c -> {
c.description("Some description");
}));
contract {
description = """
given:
An input
when:
Sth happens
then:
Output
"""
}

名称​

你可以为你的合约提供一个名称。假设你提供以下名称:should register a user。如果你这样做,自动生成的测试名称将是 validate_should_register_a_user。此外,WireMock 存根中的存根名称将是 should_register_a_user.json。

important

你必须确保名称不包含任何会导致生成的测试无法编译的字符。同时请记住,如果你为多个合约提供相同的名称,你自动生成的测试将无法编译,并且你生成的存根会相互覆盖。

以下示例展示了如何为合约添加名称:

org.springframework.cloud.contract.spec.Contract.make {
name("some_special_name")
}
name: some name
Contract.make(c -> {
c.name("some name");
}));
contract {
name = "some_special_name"
}

忽略合约​

如果你想忽略某个合约,可以在插件配置中设置忽略合约列表,或在合约本身设置 ignored 属性。以下示例展示了具体操作方法:

org.springframework.cloud.contract.spec.Contract.make {
ignored()
}
ignored: true
Contract.make(c -> {
c.ignored();
}));
contract {
ignored = true
}

进行中的合约​

进行中的合约不会在生产者端生成测试,但允许生成存根。

important

请谨慎使用此功能,因为它可能导致误报,因为你为消费者生成了存根供其使用,而实际上并未实现相应的功能。

如果你想设置一个合约进行中,以下示例展示了如何操作:

org.springframework.cloud.contract.spec.Contract.make {
inProgress()
}
inProgress: true
Contract.make(c -> {
c.inProgress();
}));
contract {
inProgress = true
}

你可以设置Spring Cloud Contract插件的failOnInProgress属性值,以确保当源代码中至少存在一个进行中的契约时,构建会失败。

从文件传递值​

从 1.2.0 版本开始,你可以从文件中传递值。假设你的项目中有以下资源:

└── src
    └── test
       └── resources
          └── contracts
    ├── readFromFile.groovy
    ├── request.json
    └── response.json

进一步假设你的合约如下:

/*
* Copyright 2013-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import org.springframework.cloud.contract.spec.Contract

Contract.make {
request {
method('PUT')
headers {
contentType(applicationJson())
}
body(file("request.json"))
url("/1")
}
response {
status OK()
body(file("response.json"))
headers {
contentType(applicationJson())
}
}
}
request:
method: GET
url: /foo
bodyFromFile: request.json
response:
status: 200
bodyFromFile: response.json
import java.util.Collection;
import java.util.Collections;
import java.util.function.Supplier;

import org.springframework.cloud.contract.spec.Contract;

class contract_rest_from_file implements Supplier<Collection<Contract>> {

@Override
public Collection<Contract> get() {
return Collections.singletonList(Contract.make(c -> {
c.request(r -> {
r.url("/foo");
r.method(r.GET());
r.body(r.file("request.json"));
});
c.response(r -> {
r.status(r.OK());
r.body(r.file("response.json"));
});
}));
}

}
import org.springframework.cloud.contract.spec.ContractDsl.Companion.contract

contract {
request {
url = url("/1")
method = PUT
headers {
contentType = APPLICATION_JSON
}
body = bodyFromFile("request.json")
}
response {
status = OK
body = bodyFromFile("response.json")
headers {
contentType = APPLICATION_JSON
}
}
}

进一步假设 JSON 文件如下:

{
"status": "REQUEST"
}
{
"status": "RESPONSE"
}

当测试或存根生成发生时,request.json 和 response.json 文件的内容会被传递到请求或响应的正文中。文件名必须是相对于合约所在文件夹位置的文件。

如果需要以二进制形式传递文件内容,你可以在编码DSL中使用fileAsBytes方法,或在YAML中使用bodyFromFileAsBytes字段。

以下示例展示了如何传递二进制文件的内容:

import org.springframework.cloud.contract.spec.Contract

Contract.make {
request {
url("/1")
method(PUT())
headers {
contentType(applicationOctetStream())
}
body(fileAsBytes("request.pdf"))
}
response {
status 200
body(fileAsBytes("response.pdf"))
headers {
contentType(applicationOctetStream())
}
}
}
request:
url: /1
method: PUT
headers:
Content-Type: application/octet-stream
bodyFromFileAsBytes: request.pdf
response:
status: 200
bodyFromFileAsBytes: response.pdf
headers:
Content-Type: application/octet-stream
import java.util.Collection;
import java.util.Collections;
import java.util.function.Supplier;

import org.springframework.cloud.contract.spec.Contract;

class contract_rest_from_pdf implements Supplier<Collection<Contract>> {

@Override
public Collection<Contract> get() {
return Collections.singletonList(Contract.make(c -> {
c.request(r -> {
r.url("/1");
r.method(r.PUT());
r.body(r.fileAsBytes("request.pdf"));
r.headers(h -> {
h.contentType(h.applicationOctetStream());
});
});
c.response(r -> {
r.status(r.OK());
r.body(r.fileAsBytes("response.pdf"));
r.headers(h -> {
h.contentType(h.applicationOctetStream());
});
});
}));
}

}
import org.springframework.cloud.contract.spec.ContractDsl.Companion.contract

contract {
request {
url = url("/1")
method = PUT
headers {
contentType = APPLICATION_OCTET_STREAM
}
body = bodyFromFileAsBytes("contracts/request.pdf")
}
response {
status = OK
body = bodyFromFileAsBytes("contracts/response.pdf")
headers {
contentType = APPLICATION_OCTET_STREAM
}
}
}
important

当您需要处理二进制负载时,无论是HTTP还是消息传递,都应采用此方法。

元数据​

你可以向合约中添加 metadata。通过元数据,你可以将配置传递给扩展。下面是一个使用 wiremock 键的示例。它的值是一个映射,其键为 stubMapping,值为 WireMock 的 StubMapping 对象。Spring Cloud Contract 能够用你的自定义代码修补生成的存根映射的部分内容。你可能希望这样做,以便添加 webhooks、自定义延迟或与第三方 WireMock 扩展集成。

Contract.make(c -> {
c.metadata(MetadataUtil.map()
.entry("wiremock", ContractVerifierUtil.map()
.entry("stubMapping", "{ \"response\" : { \"fixedDelayMilliseconds\" : 2000 } }")));
}));
contract {
metadata("wiremock" to ("stubmapping" to """
{
"response" : {
"fixedDelayMilliseconds": 2000
}
}"""))
}

在以下章节中,您可以找到支持的元数据条目的示例。

HTTP 合约​

Spring Cloud Contract 允许您验证使用 REST 或 HTTP 作为通信手段的应用程序。Spring Cloud Contract 会验证,对于符合合约 request 部分标准的请求,服务器是否提供了与合约 response 部分一致的响应。随后,这些合约被用于生成 WireMock 存根,这些存根会为任何符合提供标准的请求提供合适的响应。