MocMVC дает HttpMessageNotReadableException
Я все еще изучаю свой путь вокруг тестирования, и я пытаюсь заставить тест MockMvc работать на меня. Это простой контроллер REST, который на данный момент выполняет только некоторую аутентификацию, используя информацию из JSON в сообщении. Я фактически реализовал код, поэтому я знаю, что он работает, потому что я получаю правильный ответ с правильным вводом и сообщения об ошибках, которые я собрал, как в формате json. Моя проблема заключается в том, что тест не справляется с HttpMessageNotReadableException, даже если фактический код работает, поэтому я предполагаю, что у меня нет правильного теста. Любую помощь вы можете дать бы здорово.
вот мой контроллер
@Controller
public class RequestPaymentController {
protected final Log logger = LogFactory.getLog(getClass());
private PaymentService paymentService;
private LoginService loginService;
@Autowired
public void setPaymentService(PaymentService paymentService){
this.paymentService = paymentService;
}
@Autowired
public void setLoginService(LoginService loginService){
this.loginService = loginService;
}
@RequestMapping(value = "/requestpayment", method = RequestMethod.POST, headers="Accept=application/json")
@ResponseBody
public ResponseEntity<PaymentResult> handleRequestPayment(@RequestBody PaymentRequest paymentRequest, HttpServletRequest request, HttpServletResponse response, BindingResult result) throws Exception{
ResponseEntity<PaymentResult> responseEntity = null;
new LoginValidator().validate(paymentRequest, result);
boolean valid = loginService.isLoginValid(paymentRequest, result);
if (valid){
responseEntity = setValidResponse(paymentRequest);
}else {
throw new TumsException("exception message");
}
return responseEntity;
}
private ResponseEntity<PaymentResult> setValidResponse(PaymentRequest paymentRequest){
PaymentResult paymentResult = paymentService.getResults(paymentRequest);
return new ResponseEntity<PaymentResult>(paymentResult, HttpStatus.OK);
}
}
и вот мой тестовый код:
public class RequestPaymentControllerTest {
PaymentService mockPaymentService;
RequestPaymentController requestPaymentController;
HttpServletRequest mockHttpServletRequest;
HttpServletResponse mockHttpServletResponse;
PaymentRequest mockPaymentRequest;
BindingResult mockBindingResult;
LoginService mockLoginService;
PaymentResult mockPaymentResult;
MockMvc mockMvc;
@Before
public void setUp() throws Exception {
mockPaymentService = createMock(PaymentService.class);
mockHttpServletRequest = createMock(HttpServletRequest.class);
mockHttpServletResponse = createMock(HttpServletResponse.class);
mockPaymentRequest = createMock(PaymentRequest.class);
requestPaymentController = new RequestPaymentController();
mockBindingResult = createMock(BindingResult.class);
mockLoginService = createMock(LoginService.class);
requestPaymentController.setPaymentService(mockPaymentService);
mockPaymentResult = createMock(PaymentResult.class);
mockMvc = MockMvcBuilders.standaloneSetup(new RequestPaymentController()).build();
}
@After
public void tearDown() throws Exception {
mockPaymentService = null;
mockHttpServletRequest = null;
mockHttpServletResponse = null;
mockPaymentRequest = null;
requestPaymentController = null;
mockBindingResult = null;
mockLoginService = null;
mockPaymentResult = null;
mockMvc = null;
}
@Test
public void testHandleRequestPayment() throws Exception{
initializeStateForHandleRequestPayment();
createExpectationsForHandleRequestPayment();
replayAndVerifyExpectationsForHandleRequestPayment();
}
private void initializeStateForHandleRequestPayment(){
}
private void createExpectationsForHandleRequestPayment(){
mockPaymentRequest.getServiceUsername();
expectLastCall().andReturn("testuser");
mockPaymentRequest.getServicePassword();
expectLastCall().andReturn("password1!");
mockLoginService.isLoginValid(mockPaymentRequest,mockBindingResult);
expectLastCall().andReturn(true);
mockPaymentService.getResults(mockPaymentRequest);
expectLastCall().andReturn(mockPaymentResult);
}
private void replayAndVerifyExpectationsForHandleRequestPayment() throws Exception{
replay(mockPaymentService, mockBindingResult, mockHttpServletRequest, mockHttpServletResponse, mockPaymentRequest, mockLoginService);
requestPaymentController.setLoginService(mockLoginService);
requestPaymentController.handleRequestPayment(mockPaymentRequest, mockHttpServletRequest, mockHttpServletResponse, mockBindingResult);
mockMvc.perform(post("/requestpayment")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isBadRequest());
verify(mockPaymentService, mockBindingResult, mockHttpServletRequest, mockHttpServletResponse, mockPaymentRequest, mockLoginService);
}
}
результаты andDo (print ()) являются следующими:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /requestpayment
Parameters = {}
Headers = {Content-Type=[application/json], Accept=[application/json]}
Handler:
Type = portal.echecks.controller.RequestPaymentController
Method = public org.springframework.http.ResponseEntity<portal.echecks.model.PaymentResult> portal.echecks.controller.RequestPaymentController.handleRequestPayment(portal.echecks.model.PaymentRequest,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,org.springframework.validation.BindingResult) throws java.lang.Exception
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotReadableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
MockHttpServletResponse:
Status = 400
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Process finished with exit code 0
как вы можете видеть, тест проходит, когда я ожидаю плохой статус запроса, но я поставил в журнал, и я знаю, что ResponseBody, который я отправляю обратно, имеет 200 статус. Как я уже сказал, Это мой первый раз с MockMvc, поэтому я предполагаю, что я не настроил что-то правильно. Есть предложения?
2 ответов
An HttpMessageNotReadableException
is
брошенный реализациями HttpMessageConverter, когда метод read неудачи.
вы также получаете 400 плохой запрос в вашем ответе. Все это должно сказать вам, что вы не отправляете то, что ожидает ваш сервер. Чего ожидает ваш сервер?
@RequestMapping(value = "/requestpayment", method = RequestMethod.POST, headers="Accept=application/json")
@ResponseBody
public ResponseEntity<PaymentResult> handleRequestPayment(@RequestBody PaymentRequest paymentRequest, HttpServletRequest request, HttpServletResponse response, BindingResult result) throws Exception{
главное здесь-это Примеч. Таким образом, вы говорите своему серверу попытаться десериализовать PaymentRequest
экземпляр из организма запроса HTTP POST.
Итак, давайте посмотрим запрос, который вы делаете
mockMvc.perform(post("/requestpayment")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isBadRequest());
я не вижу, чтобы вы предоставляли тело для запроса. Должно быть content(String)
вызов где-то там, чтобы установить содержимое запроса POST. Это содержимое должно быть сериализацией json PaymentRequest
.
обратите внимание, что поскольку вы используете StandaloneMockMvcBuilder
, вам может потребоваться установить HttpMessageConverter
экземпляры сами, т. е. а MappingJackson2HttpMessageConverter
для сериализации и десериализации формат JSON.
отметим, что BindingResult
параметр должен появиться сразу после параметра, с которым он связан. Вот так!--16-->
@RequestMapping(value = "/requestpayment", method = RequestMethod.POST, headers="Accept=application/json")
@ResponseBody
public ResponseEntity<PaymentResult> handleRequestPayment(@Valid @RequestBody PaymentRequest paymentRequest, BindingResult result, HttpServletRequest request, HttpServletResponse response) throws Exception{
не забудьте @Valid
.
обратите внимание, что это
requestPaymentController.setLoginService(mockLoginService);
requestPaymentController.handleRequestPayment(mockPaymentRequest, mockHttpServletRequest, mockHttpServletResponse, mockBindingResult);
полностью не связан с MockMvc
тест, который вы делаете.
в моем случае, как sprint mvc w/ jackson (jackson-mapper-asl, v-1.9.10) десериализация требует парсера JSON. И Джексон требует конструктора по умолчанию для десериализации сообщений http-запроса, если нет конструктора по умолчанию, Джексон будет иметь проблему с отражением и выдает исключение HttpMessageNotReadableException.
Это значит,все классы / подклассы, которые использовались в качестве тела запроса (в этом случае), требуют конструктора по умолчанию. Это стоило мне через несколько минут после того, как я попытался добавить пользовательский конвертер и другие предложения, которые я получил в stackoverflow напрасно.
или вы можете добавлять пользовательские десериализатор или миксин аннотации, чтобы избежать добавления конструктор по умолчанию hierachically везде. как описано здесь: http://blogs.jbisht.com/blogs/2016/09/12/Deserialize-json-with-Java-parameterized-constructor. Проверьте это, если вам интересно.
кажется, здесь дублируется > Весна HttpMessageNotReadableException.