使用 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 流程:@AutoConfigureRestDocsspring-doc.cn

rest-docs

以下示例使用 :MockMvcspring-doc.cn

@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.jsonGET/resourcespring-doc.cn

@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 的正文),我们需要 显式创建请求匹配器。这样做有两个效果:hostcontent-lengthspring-doc.cn

此功能的主要入口点是 ,可以使用 作为便捷方法的替代方法,如下所示 示例显示:WireMockRestDocs.verify()document()spring-doc.cn

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()spring-doc.cn

除了 and 方便方法,您还可以使用 WireMock API 验证请求是否与创建的存根匹配,因为 以下示例显示:jsonPathcontentTypespring-doc.cn

@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 路径。您可以使用这些功能创建具有更宽 参数范围。前面的示例生成类似于以下示例的存根:spring-doc.cn

post-resource.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")spring-doc.cn

使用 REST Docs 生成合约

您还可以使用 Spring REST 生成 Spring Cloud Contract DSL 文件和文档 文档。如果与 Spring Cloud WireMock 结合使用执行此操作,则会同时获得两个 Contract 和存根。spring-doc.cn

为什么要使用此功能?社区中的一些人提出了问题 关于他们希望转向基于 DSL 的合约定义的情况, 但是他们已经有很多 Spring MVC 测试了。使用此功能可让您生成 您稍后可以修改并移动到文件夹(在 configuration),以便插件找到它们。spring-doc.cn

您可能想知道为什么 WireMock 模块中有此功能。功能 存在,因为生成 Contract 和 Stub 都是有意义的。

请考虑以下测试:spring-doc.cn

		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))));

前面的测试会创建上一节中介绍的存根,并生成 合同和文档文件。spring-doc.cn

调用 Contract 并可能类似于以下示例:index.groovyspring-doc.cn

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.adocspring-doc.cn

指定 priority 属性

该方法采用可选的 Map 参数,该参数允许您在模板中指定其他属性。SpringCloudContractRestDocs.dslContract()spring-doc.cn

其中一个属性是 priority 字段,您可以按如下方式指定:spring-doc.cn

SpringCloudContractRestDocs.dslContract(Map.of("priority", 1))

重写 DSL 协定模板

默认情况下,协定的输出基于名为 .default-dsl-contract-only.snippetspring-doc.cn

您可以通过覆盖 getTemplate() 方法来提供自定义模板文件,如下所示:spring-doc.cn

new ContractDslSnippet(){
    @Override
    protected String getTemplate() {
        return "custom-dsl-contract";
    }
}));

所以上面的示例显示了这条线spring-doc.cn

.andDo(document("index", SpringCloudContractRestDocs.dslContract()));

应更改为:spring-doc.cn

.andDo(document("index", new ContractDslSnippet(){
                            @Override
                            protected String getTemplate() {
                                return "custom-dsl-template";
                            }
                        }));

通过在 Classpath 上查找资源来解析模板。将按顺序检查以下位置:spring-doc.cn

  • org/springframework/restdocs/templates/${templateFormatId}/${name}.snippetspring-doc.cn

  • org/springframework/restdocs/templates/${name}.snippetspring-doc.cn

  • org/springframework/restdocs/templates/${templateFormatId}/default-${name}.snippetspring-doc.cn

因此,在上面的示例中,您应该将一个名为 custom-dsl-template.snippet 的文件放入src/test/resources/org/springframework/restdocs/templates/custom-dsl-template.snippetspring-doc.cn