java - 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);
我知道我可以通过以下方式解决这个问题:
- 使用 String 并使用 Gson 反序列化,或
- 使用 RestTemplate 而不是 TestRestTemplate 并处理 HttpStatusCodeException,或
- 当状态码不是 2xx 时不尝试反序列化的覆盖方法
但由于 TestRestTemplate 应该是一个容错的便利子类,我希望它开箱即用,而不是尝试反序列化错误响应。
我在这里错过了什么吗?我用错了吗?
解决方案
很抱歉重新提出这个将近 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
.
推荐阅读
- spring-boot - 将服务传递给百里香模板是不好的做法吗?
- r - R Markdown将不同的表格打印到环境
- google-colaboratory - OSError:无法读取数据(文件读取失败:...错误消息='输入/输出错误'...)
- c - 机器人的 C 编程
- javascript - 使用“import * as jQuery”时出现“TypeError: $ is not a function”
- javascript - 我想在来自 React Native 中的 API 的数组中添加一个值
- blazor - 在 Blazor 服务器应用程序中如何以及在何处实现 JsInterop javascript 端?
- angular - Object(...) 不是 Function.DominoAdapter.makeCurrent 的函数
- laravel - 如何设置 morphTo() 参数
- c# - 创建具有多个一对多关系和 DeleteBehavior 的实体