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.