TestRestTemplate引发4xx状态代码的异常

时间:2019-02-14 17:08:18

标签: java spring spring-boot resttemplate spring-boot-test

我正在为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]

如果我将响应实体更改为接收String而不是List,则我将接收到响应,并且可以检查状态并确认它是“禁止的”

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

我知道我可以通过以下方法解决此问题:

  • 使用String并通过Gson反序列化,或
  • 使用RestTemplate代替TestRestTemplate并处理HttpStatusCodeException或
  • 当状态码不是2xx时不尝试反序列化的替代方法

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

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

3 个答案:

答案 0 :(得分:0)

也许我不理解问题,但是为什么您没有通过使用RestClientException类捕获异常。如果不是这种情况,那么您需要尝试上述解决方法。

答案 1 :(得分:0)

我希望实现ResponseErrorHandler可以帮助您解决此问题。

但是对于 RestTemplate ,这是向不成功的结果抛出错误的默认行为,您确定尚未覆盖它吗?也许您可以使用专用的RestTemplate进行测试。

来源:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/HttpClientErrorException.html

  

接收到HTTP 4xx时引发的异常。

有关实现ResponseErrorHandler的信息,请参见https://www.baeldung.com/spring-rest-template-error-handling

编辑:实际上,对于 TestRestTemplate ,这不是默认行为,它意味着具有以下优点的集成测试:

  

模板通过不对服务器端错误引发异常来以一种易于测试的方式运行
  ...

     
      
  • 不遵循重定向(因此您可以声明响应位置)。
  •   
  • 将忽略Cookie(因此模板是无状态的)。
  •   

就您而言,您在测试代码中保证会返回用户列表,但事实并非如此,我不希望代码对此具有弹性,我什至会说{{1} }可能更有意义。

答案 2 :(得分:0)

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

正如 Martin 在他的回答中提到的,TestRestTemplate 不包括通常与正确的 ResponseErrorHandler 相关联的 RestTemplate。但响应正文仍将包含错误消息而不是 User 列表。

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

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

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

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")) 中的类型。