如何在MockMvc POST请求测试中获取响应体?
MockMvc POST测试无响应体问题排查与解决
问题场景
在Spring Boot的Controller集成测试中,发送POST请求后无法获取响应体,报错com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input,响应体为空。
相关代码
StudentController
@RestController public class StudentController { @Autowired StudentService studentService; @PostMapping("/add-student") public ResponseEntity<Student> addStudent(@RequestBody Student student) { return new ResponseEntity<>(studentService.addStudent(student), HttpStatus.CREATED); } }
StudentService
@Service public class StudentService { public Student addStudent(Student student) { return student; } }
Student实体类
public class Student { String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + '}'; } }
原测试类
@WebMvcTest class StudentControllerTest { @Autowired MockMvc mockMvc; @InjectMocks StudentController studentController; @Autowired ObjectMapper objectMapper; @MockBean private StudentService studentService; @Test void addStudent() throws Exception { Student student = new Student(); student.setName("pushpak"); Mockito.when(studentService.addStudent(student)).thenReturn(student); MockHttpServletResponse response = mockMvc .perform(post("/add-student") .content(objectMapper.writeValueAsString(student)) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) ).andReturn().getResponse(); Assertions.assertEquals(student.getName(), objectMapper.readValue(response.getContentAsString(), Student.class).getName()); } }
报错信息
MockHttpServletRequest: HTTP Method = POST Request URI = /add-student Parameters = {} Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json", Content-Length:"18"] Body = {"name":"pushpak"} Session Attrs = {} Handler: Type = com.myapp.spring_boot_test.controller.StudentController Method = com.myapp.spring_boot_test.controller.StudentController#addStudent(Student) Async: Async started = false Async result = null Resolved Exception: Type = null ModelAndView: View name = null View = null Model = null FlashMap: Attributes = null MockHttpServletResponse: Status = 201 Error message = null Headers = [] Content type = null Body = Forwarded URL = null Redirected URL = null Cookies = [] com.fasterxml.jackson.databind.exc.MismatchedInputException: No content to map due to end-of-input at [Source: (String)""; line: 1, column: 0] ...(省略栈跟踪)
问题原因
- Mockito匹配逻辑错误:测试中用
Mockito.when(studentService.addStudent(student)).thenReturn(student)进行实例精确匹配,但Spring MVC接收到JSON后会反序列化为全新的Student对象,和测试代码里的实例不是同一个,导致Mock方法未触发,Service返回null,响应体为空。 - 冗余注解冲突:
@WebMvcTest已经会自动配置Controller,手动加@InjectMocks会干扰Spring的自动注入逻辑,可能引发依赖注入异常。
解决方案
1. 修改Mockito匹配方式
使用any(Student.class)参数匹配器,匹配任意Student类型的参数:
Mockito.when(studentService.addStudent(Mockito.any(Student.class))).thenReturn(student);
2. 移除冗余注解
删除测试类中的@InjectMocks StudentController studentController;,让@WebMvcTest自动管理Controller实例。
3. 优化断言方式(可选)
用MockMvc的andExpect直接验证响应,代码更简洁:
mockMvc.perform(post("/add-student") .content(objectMapper.writeValueAsString(student)) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(jsonPath("$.name").value(student.getName()));
修改后的完整测试类
@WebMvcTest class StudentControllerTest { @Autowired MockMvc mockMvc; @Autowired ObjectMapper objectMapper; @MockBean private StudentService studentService; @Test void addStudent() throws Exception { Student student = new Student(); student.setName("pushpak"); // 使用参数匹配器 Mockito.when(studentService.addStudent(Mockito.any(Student.class))).thenReturn(student); // 方式一:直接用andExpect验证 mockMvc.perform(post("/add-student") .content(objectMapper.writeValueAsString(student)) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isCreated()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.name").value(student.getName())); // 方式二:获取响应体后验证(按需使用) /*MockHttpServletResponse response = mockMvc.perform(post("/add-student") .content(objectMapper.writeValueAsString(student)) .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON)) .andReturn().getResponse(); Assertions.assertEquals(student.getName(), objectMapper.readValue(response.getContentAsString(), Student.class).getName());*/ } }
内容的提问来源于stack exchange,提问作者Pushpak
相关产品推荐
相关产品推荐

