使用 REST 文档
您可以使用 Spring REST Docs 生成
使用 Spring MockMvc 的 HTTP API 的文档(例如,以 Asciidoc 格式),
WebTestClient 或 RestAssure 的 Recover。在为 API 生成文档的同时,您还可以
使用 Spring Cloud Contract WireMock 生成 WireMock 存根。为此,请编写
普通的 REST Docs 测试用例,并且曾经有 stub 是
在 REST Docs 输出目录中自动生成。下面的 UML 图显示了
REST Docs 流程:@AutoConfigureRestDocs
以下示例使用 :MockMvc
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureMockMvc
public class ApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void contextLoads() throws Exception {
mockMvc.perform(get("/resource"))
.andExpect(content().string("Hello World"))
.andDo(document("resource"));
}
}
此测试在 处生成 WireMock 存根。它匹配
所有请求都发送到该路径。WebTestClient 的相同示例(使用
用于测试 Spring WebFlux 应用程序)将如下所示:target/snippets/stubs/resource.json
GET
/resource
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureWebTestClient
public class ApplicationTests {
@Autowired
private WebTestClient client;
@Test
public void contextLoads() throws Exception {
client.get().uri("/resource").exchange()
.expectBody(String.class).isEqualTo("Hello World")
.consumeWith(document("resource"));
}
}
无需任何其他配置,这些测试将创建一个带有请求匹配器的存根
对于 HTTP 方法以及除 和 之外的所有标头。要匹配
request 更精确地(例如,为了匹配 POST 或 PUT 的正文),我们需要
显式创建请求匹配器。这样做有两个效果:host
content-length
-
创建仅以指定方式匹配的存根。
-
断言测试用例中的请求也匹配相同的条件。
此功能的主要入口点是 ,可以使用
作为便捷方法的替代方法,如下所示
示例显示:WireMockRestDocs.verify()
document()
import static org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocs.verify;
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureMockMvc
public class ApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void contextLoads() throws Exception {
mockMvc.perform(post("/resource")
.content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
.andExpect(status().isOk())
.andDo(verify().jsonPath("$.id"))
.andDo(document("resource"));
}
}
前面的 Contract 指定任何带有字段的有效 POST 都会收到响应
在此测试中定义。您可以将调用链接在一起以添加其他
匹配器。如果不熟悉 JSON 路径,则 JayWay
文档可以帮助您快速上手。此测试的 WebTestClient 版本
具有插入到同一位置的类似 static 辅助对象。id
.jsonPath()
verify()
除了 and 方便方法,您还可以使用
WireMock API 验证请求是否与创建的存根匹配,因为
以下示例显示:jsonPath
contentType
@Test
public void contextLoads() throws Exception {
mockMvc.perform(post("/resource")
.content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
.andExpect(status().isOk())
.andDo(verify()
.wiremock(WireMock.post(urlPathEquals("/resource"))
.withRequestBody(matchingJsonPath("$.id"))
.andDo(document("post-resource"))));
}
WireMock API 非常丰富。您可以通过以下方式匹配标头、查询参数和请求正文 regex 以及 JSON 路径。您可以使用这些功能创建具有更宽 参数范围。前面的示例生成类似于以下示例的存根:
{
"request" : {
"url" : "/resource",
"method" : "POST",
"bodyPatterns" : [ {
"matchesJsonPath" : "$.id"
}]
},
"response" : {
"status" : 200,
"body" : "Hello World",
"headers" : {
"X-Application-Context" : "application:-1",
"Content-Type" : "text/plain"
}
}
}
您可以使用 method 或 and 方法创建请求匹配器,但不能同时使用这两种方法。wiremock() jsonPath() contentType() |
在消费者端,您可以在本节前面进行生成的
available (例如,通过将存根发布为 JAR)。之后,您可以在
多种不同的方法,包括使用 ,如本文前面所述
公文。resource.json
@AutoConfigureWireMock(stubs="classpath:resource.json")
使用 REST Docs 生成合约
您还可以使用 Spring REST 生成 Spring Cloud Contract DSL 文件和文档 文档。如果与 Spring Cloud WireMock 结合使用执行此操作,则会同时获得两个 Contract 和存根。
为什么要使用此功能?社区中的一些人提出了问题 关于他们希望转向基于 DSL 的合约定义的情况, 但是他们已经有很多 Spring MVC 测试了。使用此功能可让您生成 您稍后可以修改并移动到文件夹(在 configuration),以便插件找到它们。
您可能想知道为什么 WireMock 模块中有此功能。功能 存在,因为生成 Contract 和 Stub 都是有意义的。 |
请考虑以下测试:
this.mockMvc
.perform(post("/foo").accept(MediaType.APPLICATION_PDF)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content("{\"foo\": 23, \"bar\" : \"baz\" }"))
.andExpect(status().isOk())
.andExpect(content().string("bar"))
// first WireMock
.andDo(WireMockRestDocs.verify()
.jsonPath("$[?(@.foo >= 20)]")
.jsonPath("$[?(@.bar in ['baz','bazz','bazzz'])]")
.contentType(MediaType.valueOf("application/json")))
// then Contract DSL documentation
.andDo(document("index", SpringCloudContractRestDocs.dslContract(Maps.of("priority", 1))));
前面的测试会创建上一节中介绍的存根,并生成 合同和文档文件。
调用 Contract 并可能类似于以下示例:index.groovy
import org.springframework.cloud.contract.spec.Contract
Contract.make {
request {
method 'POST'
url '/foo'
body('''
{"foo": 23 }
''')
headers {
header('''Accept''', '''application/json''')
header('''Content-Type''', '''application/json''')
}
}
response {
status OK()
body('''
bar
''')
headers {
header('''Content-Type''', '''application/json;charset=UTF-8''')
header('''Content-Length''', '''3''')
}
bodyMatchers {
jsonPath('$[?(@.foo >= 20)]', byType())
}
}
}
生成的文档(在本例中为 Asciidoc 格式)包含一个格式化的
合同。此文件的位置将为 。index/dsl-contract.adoc
指定 priority 属性
该方法采用可选的 Map 参数,该参数允许您在模板中指定其他属性。SpringCloudContractRestDocs.dslContract()
其中一个属性是 priority 字段,您可以按如下方式指定:
SpringCloudContractRestDocs.dslContract(Map.of("priority", 1))
重写 DSL 协定模板
默认情况下,协定的输出基于名为 .default-dsl-contract-only.snippet
您可以通过覆盖 getTemplate() 方法来提供自定义模板文件,如下所示:
new ContractDslSnippet(){
@Override
protected String getTemplate() {
return "custom-dsl-contract";
}
}));
所以上面的示例显示了这条线
.andDo(document("index", SpringCloudContractRestDocs.dslContract()));
应更改为:
.andDo(document("index", new ContractDslSnippet(){
@Override
protected String getTemplate() {
return "custom-dsl-template";
}
}));
通过在 Classpath 上查找资源来解析模板。将按顺序检查以下位置:
-
org/springframework/restdocs/templates/${templateFormatId}/${name}.snippet
-
org/springframework/restdocs/templates/${name}.snippet
-
org/springframework/restdocs/templates/${templateFormatId}/default-${name}.snippet
因此,在上面的示例中,您应该将一个名为 custom-dsl-template.snippet 的文件放入src/test/resources/org/springframework/restdocs/templates/custom-dsl-template.snippet