我还在学习测试方法,我正在尝试让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,所以我认为我没有正确设置。有什么建议?
答案 0 :(得分:13)
HttpMessageNotReadableException
是
在读取方法时由HttpMessageConverter实现抛出 失败。
您的回复中也会收到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{
这里的主要内容是@RequestBody
带注释的参数。因此,您要告诉服务器尝试从HTTP POST请求的主体中反序列化PaymentRequest
实例。
让我们看看你正在提出的要求
mockMvc.perform(post("/requestpayment")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isBadRequest());
我没有看到你为请求提供正文。在那里应该有一个content(String)
调用来设置POST请求的内容。此内容应为PaymentRequest
。
请注意,由于您使用的是StandaloneMockMvcBuilder
,因此您可能需要自己设置HttpMessageConverter
个实例,即。一个MappingJackson2HttpMessageConverter
来序列化和反序列化JSON。
请注意,BindingResult
参数应紧跟在与之相关的参数之后。像这样
@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
测试完全无关。
答案 1 :(得分:0)
就我而言,作为带有sackson的sprint mvc(jackson-mapper-asl,v-1.9.10),反序列化需要JSON解析器。杰克逊需要为HTTP请求消息反序列化使用默认的构造函数,如果没有默认的构造函数,杰克逊将遇到反射问题并抛出HttpMessageNotReadableException异常。
这就是说,用作请求正文的所有类/子类(在这种情况下)都需要默认的构造函数。在尝试添加自定义转换器和其他徒劳的stackoverflow建议之后,这花了我几分钟的时间。
或者您可以添加Custom Deserializer或Mixin批注,以避免在各处分层地添加默认构造函数。如此处所述:http://blogs.jbisht.com/blogs/2016/09/12/Deserialize-json-with-Java-parameterized-constructor。如果您有兴趣,请检查此内容。
似乎在此处重复> Spring HttpMessageNotReadableException。
答案 2 :(得分:0)
确保满足以下条件:
@ResponseBody
批注在单元测试中
@ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {....}) @WebMvcTest @AutoConfigureMockMvc
答案 3 :(得分:0)
回答可能为时已晚,但以防万一有人仍在查看此页面。
正如@Sotirios Delimanolis提到的那样,问题出在错误的请求上-在参数中指定了“ @RequestBody ”,但请求主体中从未提供。因此,如果您按如下所示使用' content(someRequestString)'将其添加到请求中,则它应该可以工作。
PaymentRequest paymentRequest = new PaymentRequest(...);
String requestBody = new ObjectMapper().valueToTree(paymentRequest).toString();
mockMvc.perform(post("/requestpayment")
.content(requestBody)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("SUCCESS"))
.andExpect(jsonPath("$.paymentAmount", is(20)));
jsonPath 可用于验证响应上的属性。在上面的示例中,假设 PaymentResponse 在json响应中具有属性 status 和 paymentAmount 。这些零件可以轻松验证。
您可能会遇到类似-
的错误NoClassDefFoundError:com / jayway / jsonpath / Predicate
,同时使用jsonPath。因此,请确保将其显式添加到类路径中,因为它是 spring-test 中的可选依赖项,并且不会传递。如果使用maven,请执行以下操作:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.4.0</version>
<scope>test</scope>
</dependency>