首页 > 解决方案 > TestRestTemplate 为 4xx 状态码抛出异常

问题描述

我正在为 Spring-Boot 应用程序编写组件测试,以测试我的安全配置。因此,我正在运行应该测试成功响应和“禁止”状态的测试。我遇到的问题是,因为我的 REST 调用需要一个复杂的 JSON,所以对于阻塞的调用,测试失败,因为 TestRestTemplate 试图反序列化一个不存在的响应主体。

我正在运行一个 Spring-Boot 应用程序,并且测试类注释为:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

我正在尝试测试应该返回用户列表的 REST API。调用的简化版本是:

ResponseEntity<List<User>> responseEntity  = testRestTemplate.exchange(URL, HttpMethod.GET, entity, new ParameterizedTypeReference<List<User>>() {});

其中 TestRestTemplate 由 Spring 自动装配,并且实体包含授权信息。

对于未经授权的请求,我收到如下错误:

org.springframework.web.client.RestClientException: Error while extracting response for type [java.util.List<my.package.User>] and content type [application/json;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.util.ArrayList` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList` out of START_OBJECT token
 at [Source: (PushbackInputStream); line: 1, column: 1]

如果我将响应实体更改为接收字符串而不是列表,我会收到响应并可以检查状态并看到它是“禁止的”

ResponseEntity<String> responseEntity  = testRestTemplate.exchange(URL, HttpMethod.GET, null, String.class);

我知道我可以通过以下方式解决这个问题:

但由于 TestRestTemplate 应该是一个容错的便利子类,我希望它开箱即用,而不是尝试反序列化错误响应。

我在这里错过了什么吗?我用错了吗?

标签: javaspringspring-bootresttemplatespring-boot-test

解决方案


很抱歉重新提出这个将近 2 年的问题,但我在使用 SpringTestRestTemplate和否定验证测试时遇到了一个非常相似的问题。

正如马丁在他的回答中提到的,TestRestTemplate不包括ResponseErrorHandler通常与适当的RestTemplate. 但响应的正文仍将包含一条错误消息,而不是一个User.

就我而言,我的网络应用程序@ControllerAdvice包含了所有常见的验证错误(MethodArgumentNotValidException,MethodArgumentTypeMismatchException等)并返回了我自己的类的实例ErrorMessageDto。控制器会将其编组为 JSON 而不是预期的响应。

我的组件测试最初试图捕获HttpStatusCodeException,因为它是由正常的RestTemplate. 在测试中,没有抛出异常(因为缺少ResponseErrorHandler),我restTemplate.postForObject(path, request, MyResponse.class)只是返回了一个空版本的MyResponse.

在阅读了马丁的描述和以下链接后,我将其更改为

ResponseEntity<ErrorMessageDto> responseEntity = restTemplate.postForEntity(path, request, ErrorMessageDto.class);

// Do assertions on the response code and body (using assertj)
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.UNPROCESSABLE_ENTITY);
assertThat(responseEntity.getBody().getErrors())
                .extracting("path", "message")
                .contains(tuple("firstName", "size must be between 0 and 255"))

在您的情况下,我确信您返回的错误消息是错误消息类的实例。您可能通过返回字符串并手动编组的建议意识到了这一点。如果你知道你的错误消息代表什么类,你可以简单地将它替换为你的ResponseEntity.


推荐阅读