HTTP Contracts
此页面描述了契约中最重要的 HTTP 相关部分。
HTTP Top-Level Elements
HTTP Top-Level Elements
可以在契约定义的顶层闭包中调用以下方法:
-
request
: Mandatory -
response
: Mandatory -
priority
: Optional
以下示例展示了如何定义一个 HTTP 请求契约:
Unresolved directive in _dsl-http-top-level-elements.adoc - include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[]
Unresolved directive in _dsl-http-top-level-elements.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
Unresolved directive in _dsl-http-top-level-elements.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
...
Unresolved directive in _dsl-http-top-level-elements.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
...
Unresolved directive in _dsl-http-top-level-elements.adoc - include::{verifier_root_path}/src/test/resources/contractsToCompile/contract_docs_examples.java[]
Unresolved directive in _dsl-http-top-level-elements.adoc - include::{verifier_root_path}/src/test/resources/kotlin/contract_docs_examples.kts[]
如果你想让你的合同有更高的优先级,你需要给 priority
标签或方法传递一个较小的数值。例如,值为 5
的 priority
比值为 10
的 priority
具有更高的优先级。
HTTP Request
HTTP Request
HTTP 协议仅要求在请求中指定方法和 URL。相同的的信息在契约的请求定义中是强制性的。
以下示例展示了一个请求契约:
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/contractsToCompile/contract_docs_examples.java[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/kotlin/contract_docs_examples.kts[]
你可以指定一个绝对而不是相对 url
,但是使用 urlPath
是推荐的方式,因为这样做使测试独立于主机。
以下示例使用了 url
:
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/yml/contract_rest_with_path.yml[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/contractsToCompile/contract_docs_examples.java[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/kotlin/contract_docs_examples.kts[]
request
可能包含查询参数,如下例所示(该示例使用了 urlPath
):
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
...
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/contractsToCompile/contract_docs_examples.java[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/kotlin/contract_docs_examples.kts[]
如果一个查询参数在合同中丢失,并不意味着我们期望在查询参数丢失的情况下匹配请求。恰恰相反,这意味着查询参数不存在并不要求匹配请求。
request
可能包含额外的请求头,如下例所示:
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
...
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/contractsToCompile/contract_docs_examples.java[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/kotlin/contract_docs_examples.kts[]
request
可能包含额外的请求 cookie,如下例所示:
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
...
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/contractsToCompile/contract_docs_examples.java[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/kotlin/contract_docs_examples.kts[]
request
可能包含一个请求正文,如下例所示:
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
...
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/contractsToCompile/contract_docs_examples.java[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/kotlin/contract_docs_examples.kts[]
request
可能包含多部分元素。要包含多部分元素,请使用 multipart
方法/章节,如下例所示:
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/SpringTestMethodBodyBuildersSpec.groovy[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/yml/contract_multipart.yml[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/contractsToCompile/contract_multipart.java[]
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/resources/kotlin/multipart.kts[]
在前面的示例中,我们通过以下两种方式定义了参数:
-
直接通过使用映射符号,值可以是动态属性(如
formParameter: $(consumer(…​), producer(…​))
)。 -
通过使用
named(…​)
让你能够设置一个命名参数的方法。一个命名参数能够设置name
和content
。你可以通过使用一个像named("fileName", "fileContent")
这样一个带有两个参数的方法,或者通过使用一个像named(name: "fileName", content: "fileContent")
这样的映射符号来调用它。
-
多部分参数在
multipart.params
部分设置。 -
命名参数(对于一个给定的参数名称的
fileName
和fileContent
)能够在multipart.named
部分设置。该部分包含paramName
(参数名称)、fileName
(文件名称)、fileContent
(文件内容)字段。 -
可以在
matchers.multipart
部分设置动态位。-
对于参数,使用
params
部分,该部分能够接受regex
或一个predefined
正则表达式。 -
对于命名参数,使用
named
部分,首先在里面使用paramName
定义参数名称。然后你可以传递fileName
或fileContent
参数化或者regex
或predefined
正则表达式。
-
对于 named(…)
部分,您始终需要添加一对 value(producer(…), consumer(…))
调用。只设置 DSL 属性(如只设置 value(producer(…))
或只设置 file(…)
)不起作用。有关更多信息,请查看此 issue。
从上例中的契约来看,生成的测试和存根如下所示:
// given:
MockMvcRequestSpecification request = given()
.header("Content-Type", "multipart/form-data;boundary=AaB03x")
.param("formParameter", "\"formParameterValue\"")
.param("someBooleanParameter", "true")
.multiPart("file", "filename.csv", "file content".getBytes());
// when:
ResponseOptions response = given().spec(request)
.put("/multipart");
// then:
assertThat(response.statusCode()).isEqualTo(200);
Unresolved directive in _dsl-request.adoc - include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/dsl/wiremock/WireMockGroovyDslSpec.groovy[]
HTTP Response
HTTP Response
响应必须包含一个 HTTP 状态代码,并且可以包含其他信息。以下代码显示一个示例:
Unresolved directive in _dsl-response.adoc - include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/ContractHttpDocsSpec.groovy[]
Unresolved directive in _dsl-response.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
...
Unresolved directive in _dsl-response.adoc - include::{verifier_root_path}/src/test/resources/yml/contract.yml[]
Unresolved directive in _dsl-response.adoc - include::{verifier_root_path}/src/test/resources/contractsToCompile/contract_docs_examples.java[]
Unresolved directive in _dsl-response.adoc - include::{verifier_root_path}/src/test/resources/kotlin/contract_docs_examples.kts[]
除了状态之外,响应可能包含标头、Cookie 和正文,以与请求中指定的方式相同(请参阅 HTTP Request)。
在 Groovy DSL 中,你可以引用 |
XML Support for HTTP
XML Support for HTTP
对于 HTTP 合同,我们还支持在请求和响应正文中使用 XML。XML 正文必须在 body
元素中作为 String
或 GString
传递。此外,可以为请求和响应提供正文匹配器。在 jsonPath(…)
方法处,应使用 org.springframework.cloud.contract.spec.internal.BodyMatchers.xPath
方法,以第一个参数的形式提供所需 xPath
,并以第二个参数的形式提供适当的 MatchingType
。除 byType()
外的所有正文匹配器都受支持。
以下示例显示了一个在响应正文中包含 XML 的 Groovy DSL 合同:
Unresolved directive in _dsl-xml.adoc - include::{verifier_root_path}/src/test/groovy/org/springframework/cloud/contract/verifier/builder/XmlMethodBodyBuilderSpec.groovy[]
Unresolved directive in _dsl-xml.adoc - include::{verifier_root_path}/src/test/resources/yml/contract_rest_xml.yml[]
Unresolved directive in _dsl-xml.adoc - include::{verifier_root_path}/src/test/resources/contractsToCompile/contract_xml.java[]
Unresolved directive in _dsl-xml.adoc - include::{verifier_root_path}/src/test/resources/kotlin/contract_xml.kts[]
以下示例显示了一个为响应正文中的 XML 自动生成的测试:
@Test
public void validate_xmlMatches() throws Exception {
// given:
MockMvcRequestSpecification request = given()
.header("Content-Type", "application/xml");
// when:
ResponseOptions response = given().spec(request).get("/get");
// then:
assertThat(response.statusCode()).isEqualTo(200);
// and:
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance()
.newDocumentBuilder();
Document parsedXml = documentBuilder.parse(new InputSource(
new StringReader(response.getBody().asString())));
// and:
assertThat(valueFromXPath(parsedXml, "/test/list/elem/text()")).isEqualTo("abc");
assertThat(valueFromXPath(parsedXml,"/test/list/elem[2]/text()")).isEqualTo("def");
assertThat(valueFromXPath(parsedXml, "/test/duck/text()")).matches("[0-9]\{3}");
assertThat(nodeFromXPath(parsedXml, "/test/duck/xxx")).isNull();
assertThat(valueFromXPath(parsedXml, "/test/alpha/text()")).matches("[\\p\{L}]*");
assertThat(valueFromXPath(parsedXml, "/test/*/complex/text()")).isEqualTo("foo");
assertThat(valueFromXPath(parsedXml, "/test/duck/@type")).isEqualTo("xtype");
}
XML Support for Namespaces
支持命名空间 XML。但是,用于选择命名空间内容的任何 XPath 表达式都必须更新。
考虑以下显式命名空间 XML 文档:
<ns1:customer xmlns:ns1="http://demo.com/customer">
<email>customer@test.com</email>
</ns1:customer>
用于选择电子邮件地址的 XPath 表达式为:/ns1:customer/email/text()
。
请当心,因为不合格的表达式 (/customer/email/text()
) 导致 ""
。
对于使用不合格命名空间的内容,该表达式更加详细。考虑以下使用不合格命名空间的 XML 文档:
<customer xmlns="http://demo.com/customer">
<email>customer@test.com</email>
</customer>
用于选择电子邮件地址的 XPath 表达式为
*/[local-name()='customer' and namespace-uri()='http://demo.com/customer']/*[local-name()='email']/text()
请注意,因为不合格的表达式 (/customer/email/text()
或 */[local-name()='customer' and namespace-uri()='http://demo.com/customer']/email/text()
) 会导致 ""
。即使是子元素也必须使用 local-name
语法引用。
Asynchronous Support
Asynchronous Support
如果你在服务器端使用异步通信(你的控制器返回 Callable
、DeferredResult
等),则必须在合同中在 response
部分提供一个 async()
方法。以下代码显示了一个示例:
org.springframework.cloud.contract.spec.Contract.make {
request {
method GET()
url '/get'
}
response {
status OK()
body 'Passed'
async()
}
}
response:
async: true
class contract implements Supplier<Collection<Contract>> {
@Override
public Collection<Contract> get() {
return Collections.singletonList(Contract.make(c -> {
c.request(r -> {
// ...
});
c.response(r -> {
r.async();
// ...
});
}));
}
}
import org.springframework.cloud.contract.spec.ContractDsl.Companion.contract
contract {
request {
// ...
}
response {
async = true
// ...
}
}
你还可以使用 fixedDelayMilliseconds
方法或属性为你的存根添加延迟。以下示例演示如何执行此操作:
org.springframework.cloud.contract.spec.Contract.make {
request {
method GET()
url '/get'
}
response {
status 200
body 'Passed'
fixedDelayMilliseconds 1000
}
}
response:
fixedDelayMilliseconds: 1000
class contract implements Supplier<Collection<Contract>> {
@Override
public Collection<Contract> get() {
return Collections.singletonList(Contract.make(c -> {
c.request(r -> {
// ...
});
c.response(r -> {
r.fixedDelayMilliseconds(1000);
// ...
});
}));
}
}
import org.springframework.cloud.contract.spec.ContractDsl.Companion.contract
contract {
request {
// ...
}
response {
delay = fixedMilliseconds(1000)
// ...
}
}