如何在集成测试中使用RestTemplate验证Wiremock的Fault代码?
看起来你在测试Spring Cloud Contract Stub的Fault场景时遇到了点小问题——毕竟Fault和普通的HTTP状态码验证逻辑不太一样,我来给你捋清楚怎么处理。
首先得明确一个关键:当你给Stub配置了withFault(Fault.MALFORMED_RESPONSE_CHUNK)这类规则时,Stub不会返回正常的ResponseEntity。它会模拟一个异常的响应(比如格式错误的分块响应),导致你的RestTemplate.postForEntity()调用直接抛出异常,而不是返回一个可用的响应对象。这也是你原代码里assertThat(actualResponse, is(fault))无法工作的原因——actualResponse根本不会被正常赋值。
接下来是具体的解决方案,分两步走:
1. 捕获Stub触发的异常并验证Fault类型
你需要修改测试逻辑,从“获取响应后断言”改成“捕获预期异常并验证异常类型”。不同的Fault枚举值会触发不同的客户端异常,比如MALFORMED_RESPONSE_CHUNK会导致MalformedChunkCodingException(被RestClientException包装)。
修改后的测试代码示例:
@Test public void shouldReturnSuitableStatusCodeForScenario(String requestFileName, Fault expectedFault, String actualResponseFileName) throws JSONException { // Given String createRequest = readJsonFromFile(DIRECTORY, requestFileName); // When & Then Assertions.assertThrows(RestClientException.class, () -> { // 调用Stub,预期会抛出异常 stub.postForEntity(HTTP_LOCALHOST_8081 + "/v1/transaction/", createRequest, String.class); }, "Expected a fault of type " + expectedFault + " but no exception was thrown"); // 进一步验证具体的异常类型,匹配预期的Fault try { stub.postForEntity(HTTP_LOCALHOST_8081 + "/v1/transaction/", createRequest, String.class); fail("Exception was expected but not thrown"); } catch (RestClientException ex) { switch (expectedFault) { case MALFORMED_RESPONSE_CHUNK: assertThat(ex.getCause(), instanceOf(MalformedChunkCodingException.class)); break; case CONNECTION_RESET: assertThat(ex.getCause(), instanceOf(ConnectionResetException.class)); break; case BAD_GATEWAY: // 对应BadGatewayException或类似异常,根据你的客户端实现调整 assertThat(ex, instanceOf(HttpServerErrorException.BadGateway.class)); break; default: fail("Unsupported fault type: " + expectedFault); } } }
2. 关于响应体的验证(可选)
需要注意:部分Fault场景下,Stub返回的响应本身是格式错误的(比如MALFORMED_RESPONSE_CHUNK),RestTemplate根本无法正常解析响应体,所以你原代码里的JSONAssert.assertEquals()可能无法执行。
如果确实需要验证Stub返回的原始响应内容,你可以给RestTemplate添加一个拦截器,在异常发生前捕获原始的响应数据:
@BeforeEach void setUp() { stub = new RestTemplate(); // 添加拦截器捕获原始响应 stub.getInterceptors().add((request, body, execution) -> { ClientHttpResponse response = execution.execute(request, body); // 记录原始响应内容到变量,方便后续验证 String rawBody = StreamUtils.copyToString(response.getBody(), StandardCharsets.UTF_8); // 注意:这里需要把响应体重新包装回去,否则后续处理会报错 response = new BufferingClientHttpResponseWrapper(response); // 可以把rawBody存到测试类的成员变量里 this.lastRawResponse = rawBody; return response; }); }
不过要注意,像MALFORMED_RESPONSE_CHUNK这类Fault,原始响应本身就是不符合规范的,所以响应体的验证意义不大,重点还是放在异常类型的匹配上。
最后再总结一下核心要点:
- Fault场景的核心是验证客户端抛出的异常类型,而非正常响应的状态码或体
- 不同的
Fault枚举对应不同的客户端异常,需要根据实际情况调整断言逻辑 - 部分Fault场景下无法正常解析响应体,建议优先验证异常而非响应内容
内容的提问来源于stack exchange,提问作者Coder

