Spring Cloud Contract WireMock

Spring Cloud Contract WireMock 模块允许您在 Spring Boot 应用程序中使用 WireMock。有关更多详细信息,请查看 Spring Cloud Contract 仓库中的 samples 子文件夹

如果您的 Spring Boot 应用程序使用 Tomcat 作为嵌入式服务器(这是 spring-boot-starter-web 的默认设置),您可以将 spring-cloud-starter-contract-stub-runner 添加到类路径,并在测试中使用 @AutoConfigureWireMock 来使用 Wiremock。Wiremock 作为 Stub 服务器运行,您可以使用 Java API 或在测试中通过静态 JSON 声明来注册 Stub 行为。

要启动 Stub 服务器并监听不同的端口,请使用(例如)@AutoConfigureWireMock(port=9999)。对于随机端口,请使用值 0。Stub 服务器端口可以使用 wiremock.server.port 属性绑定到测试应用程序上下文。使用 @AutoConfigureWireMock 会向您的测试应用程序上下文添加一个类型为 WiremockConfiguration 的 Bean,该 Bean 在具有相同上下文的方法和类之间缓存。Spring 集成测试也是如此。此外,您可以将类型为 WireMockServer 的 Bean 注入到测试中。注册的 WireMock 服务器在每个测试类后重置。但是,如果需要在每个测试方法后重置它,请将 wiremock.reset-mappings-after-each-test 属性设置为 true

自动注册 Stub

如果您使用 @AutoConfigureWireMock,它会从文件系统或类路径(默认从 file:src/test/resources/mappings)注册 WireMock JSON Stub。您可以通过使用注解中的 stubs 属性来自定义位置,该属性可以是 Ant 风格的资源模式或目录。如果是目录,则附加 */.json。以下代码显示了一个示例

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWireMock(stubs="classpath:/stubs")
public class WiremockImportApplicationTests {

	@Autowired
	private Service service;

	@Test
	public void contextLoads() throws Exception {
		assertThat(this.service.go()).isEqualTo("Hello World!");
	}

}
实际上,WireMock 总是从 src/test/resources/mappings *以及* stubs 属性中的自定义位置加载映射。要更改此行为,您还可以指定文件根目录,如本文档的下一节所述。
此外,stubs 位置中的映射不被视为 Wiremock “默认映射”的一部分,并且测试期间对 com.github.tomakehurst.wiremock.client.WireMock.resetToDefaultMappings 的调用不会导致包含 stubs 位置中的映射。但是,org.springframework.cloud.contract.wiremock.WireMockTestExecutionListener 会在每个测试类之后重置映射(包括添加来自 stubs 位置的映射),并且可以选择在每个测试方法之后重置(由 wiremock.reset-mappings-after-each-test 属性控制)。

如果您使用 Spring Cloud Contract 的默认 stub jar,您的 stub 将存储在 /META-INF/group-id/artifact-id/versions/mappings/ 文件夹中。如果您想注册所有嵌入式 JAR 中来自该位置的所有 stub,可以使用以下语法

@AutoConfigureWireMock(port = 0, stubs = "classpath*:/META-INF...

使用文件指定 Stub Body

WireMock 可以从类路径或文件系统中的文件读取响应 Body。对于文件系统,您可以在 JSON DSL 中看到响应有一个 bodyFileName 而不是(字面的)body。文件相对于根目录解析(默认是 src/test/resources/__files)。要自定义此位置,您可以将 @AutoConfigureWireMock 注解中的 files 属性设置为父目录的位置(换句话说,__files 是一个子目录)。您可以使用 Spring 资源表示法引用 file:…​classpath:…​ 位置。不支持通用 URL。可以指定值列表——在这种情况下,WireMock 需要找到响应 Body 时将解析第一个存在的文件。

配置 files 根目录时,它也会影响 stub 的自动加载,因为它们来自根目录中的一个名为 mappings 的子目录。
files 的值对从 stubs 属性明确加载的 stub 没有影响。

备选方案:使用 JUnit Rule

对于更传统的 WireMock 体验,您可以使用 JUnit @Rules 启动和停止服务器。为此,请使用 WireMockSpring 便利类获取一个 Options 实例,如下面的示例所示

@ClassRule 表示服务器在此类中的所有方法运行后关闭。

Rest Template 的宽松 SSL 验证

WireMock 允许您使用 https URL 协议来 Stub 一个“安全”服务器。如果您的应用程序在集成测试中想要联系该 Stub 服务器,它会发现 SSL 证书无效(自安装证书的常见问题)。最佳选择通常是重新配置客户端使用 http。如果这不是一个选项,您可以让 Spring 配置一个忽略 SSL 验证错误的 HTTP 客户端(当然,仅用于测试)。

为了最大限度地减少麻烦,您需要在应用程序中使用 Spring Boot 的 RestTemplateBuilder,如下例所示

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
	return builder.build();
}

您需要 RestTemplateBuilder,因为 Builder 会通过回调传递进行初始化,这样就可以在该点设置客户端的 SSL 验证。如果您使用 @AutoConfigureWireMock 注解或 Stub Runner,这在测试中会自动发生。如果您使用 JUnit @Rule 方法,您还需要添加 @AutoConfigureHttpClient 注解,如下例所示

@RunWith(SpringRunner.class)
@SpringBootTest("app.baseUrl=https://localhost:6443")
@AutoConfigureHttpClient
public class WiremockHttpsServerApplicationTests {

	@ClassRule
	public static WireMockClassRule wiremock = new WireMockClassRule(
			WireMockSpring.options().httpsPort(6443));
...
}

如果您使用 spring-boot-starter-test,类路径中会有 Apache HTTP 客户端,并且它会被 RestTemplateBuilder 选中并配置为忽略 SSL 错误。如果您使用默认的 java.net 客户端,则不需要该注解(但添加它也无害)。目前不支持其他客户端,但可能会在未来版本中添加。

要禁用自定义的 RestTemplateBuilder,请将 wiremock.rest-template-ssl-enabled 属性设置为 false

WireMock 和 Spring MVC Mocks

Spring Cloud Contract 提供了一个便利类,可以将 JSON WireMock Stub 加载到 Spring MockRestServiceServer 中。下面的项目展示了这一点。

baseUrl 值会添加到所有 Mock 调用之前,并且 stubs() 方法接受一个 stub 路径资源模式作为参数。在上面的例子中,定义在 /stubs/resource.json 的 stub 被加载到 Mock 服务器中。如果 RestTemplate 被要求访问 example.org/,它将获得在该 URL 声明的响应。可以指定多个 stub 模式,每个模式可以是一个目录(用于递归列出所有 .json 文件)、一个固定的文件名(如上例所示)或一个 Ant 风格的模式。JSON 格式是标准的 WireMock 格式,您可以在 WireMock 网站上阅读有关信息。

目前,Spring Cloud Contract Verifier 支持 Tomcat、Jetty 和 Undertow 作为 Spring Boot 嵌入式服务器,而 Wiremock 本身对特定版本的 Jetty(当前为 9.2)有“原生”支持。要使用原生 Jetty,您需要添加原生 Wiremock 依赖并排除 Spring Boot 容器(如果存在)。