Junit中如何正确使用MockMvc测试post方法?
How to use correctly MockMvc to test post method in Junit?
我是第一次使用 MockMvc 进行单元测试,但我还没有弄清楚如何正确使用它。
我正在尝试测试一个简单的 POST 方法。
我的代码(class 代码)运行良好,我用 postman 测试了它,很明显问题出在测试代码上。
控制器:
@Controller
@RequestMapping("/employees")
public class EmployeeController {
private final EmployeeService employeeService;
private final EmployeeModelAssembler assembler;
@Autowired
public EmployeeController(EmployeeService employeeService, EmployeeModelAssembler assembler){
this.employeeService = employeeService;
this.assembler = assembler;
}
@PostMapping()
public ResponseEntity<?> addEmployee(@RequestBody Employee employee, UriComponentsBuilder builder){
EntityModel<Employee> entityModel = this.assembler.toModel(this.employeeService.addEmployee(employee));
return ResponseEntity.created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()).body(entityModel);
}
...
上面还有很多方法,但我要测试的方法是addEmployee。
测试:
@ExtendWith(SpringExtension.class)
@WebMvcTest(EmployeeController.class)
@AutoConfigureMockMvc
public class EmployeeControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private EmployeeService employeeService;
@MockBean
private EmployeeModelAssembler assembler;
@InjectMocks
private EmployeeController employeeController;
@Before
public void setUp(){
MockitoAnnotations.openMocks(this);
}
@Test
public void testAddEmployee() throws Exception {
String mockEmployeeJson =
" \"firstName\": \"new\",\n" +
" \"lastName\": \"Employee\",\n" +
" \"emailAddress\": \"new@Employee.com\",\n" +
" \"roll\": \"Software Engineer\",\n" +
" \"team\": \n" +
" {\n" +
" \"teamId\": 1\n" +
" }\n" +
"}";
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(mockEmployeeJson)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
输出:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /employees
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json", Content-Length:"171"]
Body = "firstName": "new",
"lastName": "Employee",
"emailAddress": "new@Employee.com",
"roll": "Software Engineer",
"team":
{
"teamId": 1
}
}
Session Attrs = {}
Handler:
Type = com.Ventura.Notifier.controller.EmployeeController
Method = com.Ventura.Notifier.controller.EmployeeController#addEmployee(Employee, UriComponentsBuilder)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotReadableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 400
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Status expected:<200> but was:<400>
Expected :200
Actual :400
<Click to see difference>
编辑 1
将测试更改为:
private Employee employee;
@BeforeEach
public void setUpEmployee(){
Team team = new Team();
team.setTeamId(1);
employee = new Employee();
employee.setTeam(team);
employee.setRoll("software developer");
employee.setLastName("Levi");
employee.setEmailAddress("a@a.com");
employee.setFirstName("David");
}
@Test
public void testAddEmployee() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(employee))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
但是我得到一个空指针异常。
编辑 2
我解决了空指针异常,但不确定为什么。如果有人对解决方案感兴趣:
测试:
@Test
public void testAddEmployee() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(employee))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().is2xxSuccessful());
}
并且我将 addEmployee 方法中的 return 语句更改为:
return new ResponseEntity<>(entityModel, HttpStatus.CREATED);
您的 JSON 格式输入请求正文格式不正确,我建议使用 Map
和 objectMapper
,Map.of 来自 jdk-9 如果您使用的是较低版本,您可以使用 new
关键字
创建另一个地图来替换它
@Test
public void testAddEmployee() throws Exception {
Map<String,Object> body = new HashMap<>();
body.put("firstName","new");
body.put("lastName","Employee");
body.put("emailAddress","new@Employee.com");
body.put("roll","Software Engineer");
body.put("team",Map.of("teamId",1));
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeAsString(body))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
您发布的正文实际上不是 JSON。尝试使用 ObjectMapper(例如来自 Jackson)将您的 DTO 转换为可以使用 MockMvc 发送的字符串:
Employee dto = new Employee()
// properly set your fields
[...]
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeAsString(dto))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
我是第一次使用 MockMvc 进行单元测试,但我还没有弄清楚如何正确使用它。 我正在尝试测试一个简单的 POST 方法。 我的代码(class 代码)运行良好,我用 postman 测试了它,很明显问题出在测试代码上。
控制器:
@Controller
@RequestMapping("/employees")
public class EmployeeController {
private final EmployeeService employeeService;
private final EmployeeModelAssembler assembler;
@Autowired
public EmployeeController(EmployeeService employeeService, EmployeeModelAssembler assembler){
this.employeeService = employeeService;
this.assembler = assembler;
}
@PostMapping()
public ResponseEntity<?> addEmployee(@RequestBody Employee employee, UriComponentsBuilder builder){
EntityModel<Employee> entityModel = this.assembler.toModel(this.employeeService.addEmployee(employee));
return ResponseEntity.created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()).body(entityModel);
}
...
上面还有很多方法,但我要测试的方法是addEmployee。
测试:
@ExtendWith(SpringExtension.class)
@WebMvcTest(EmployeeController.class)
@AutoConfigureMockMvc
public class EmployeeControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private EmployeeService employeeService;
@MockBean
private EmployeeModelAssembler assembler;
@InjectMocks
private EmployeeController employeeController;
@Before
public void setUp(){
MockitoAnnotations.openMocks(this);
}
@Test
public void testAddEmployee() throws Exception {
String mockEmployeeJson =
" \"firstName\": \"new\",\n" +
" \"lastName\": \"Employee\",\n" +
" \"emailAddress\": \"new@Employee.com\",\n" +
" \"roll\": \"Software Engineer\",\n" +
" \"team\": \n" +
" {\n" +
" \"teamId\": 1\n" +
" }\n" +
"}";
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(mockEmployeeJson)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
输出:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /employees
Parameters = {}
Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json", Content-Length:"171"]
Body = "firstName": "new",
"lastName": "Employee",
"emailAddress": "new@Employee.com",
"roll": "Software Engineer",
"team":
{
"teamId": 1
}
}
Session Attrs = {}
Handler:
Type = com.Ventura.Notifier.controller.EmployeeController
Method = com.Ventura.Notifier.controller.EmployeeController#addEmployee(Employee, UriComponentsBuilder)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotReadableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 400
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
java.lang.AssertionError: Status expected:<200> but was:<400>
Expected :200
Actual :400
<Click to see difference>
编辑 1 将测试更改为:
private Employee employee;
@BeforeEach
public void setUpEmployee(){
Team team = new Team();
team.setTeamId(1);
employee = new Employee();
employee.setTeam(team);
employee.setRoll("software developer");
employee.setLastName("Levi");
employee.setEmailAddress("a@a.com");
employee.setFirstName("David");
}
@Test
public void testAddEmployee() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(employee))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
但是我得到一个空指针异常。
编辑 2
我解决了空指针异常,但不确定为什么。如果有人对解决方案感兴趣:
测试:
@Test
public void testAddEmployee() throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(employee))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().is2xxSuccessful());
}
并且我将 addEmployee 方法中的 return 语句更改为:
return new ResponseEntity<>(entityModel, HttpStatus.CREATED);
您的 JSON 格式输入请求正文格式不正确,我建议使用 Map
和 objectMapper
,Map.of 来自 jdk-9 如果您使用的是较低版本,您可以使用 new
关键字
@Test
public void testAddEmployee() throws Exception {
Map<String,Object> body = new HashMap<>();
body.put("firstName","new");
body.put("lastName","Employee");
body.put("emailAddress","new@Employee.com");
body.put("roll","Software Engineer");
body.put("team",Map.of("teamId",1));
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeAsString(body))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
您发布的正文实际上不是 JSON。尝试使用 ObjectMapper(例如来自 Jackson)将您的 DTO 转换为可以使用 MockMvc 发送的字符串:
Employee dto = new Employee()
// properly set your fields
[...]
mockMvc.perform(MockMvcRequestBuilders.post("/employees")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeAsString(dto))
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());