如何正确使用 MockMvc 在 Junit 中测试 post 方法?

Posted

技术标签:

【中文标题】如何正确使用 MockMvc 在 Junit 中测试 post 方法?【英文标题】:How to use correctly MockMvc to test post method in Junit? 【发布时间】:2021-05-02 11:13:11 【问题描述】:

我是第一次使用 MockMvc 进行单元测试,但我还没有弄清楚如何正确使用它。 我正在尝试测试一个简单的 POST 方法。 我的代码(类代码)运行良好,我用邮递员测试过,很明显问题出在测试代码上。

控制器:

@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);

【问题讨论】:

【参考方案1】:

您的 JSON 格式的输入请求正文格式不正确,我建议使用 MapobjectMapper,Map.of 来自 jdk-9 如果您使用的是较低版本,您可以通过使用 @987654323 创建另一个地图来替换它@关键字

@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());

【讨论】:

您好,谢谢您的回答。我已经按照你说的做了,但是我得到了一个空指针异常,你能告诉我为什么吗?您可以在帖子底部看到编辑1,目前那里有不同的代码,但它与您所说的相似,当我尝试使用地图进行操作时,它仍然是相同的空指针异常 看看这条线EmployeeController.java:64这条线有什么? @文图拉 其实在post里面,就是addMethod的return语句:@PostMapping() public ResponseEntity> addEmployee(@RequestBody Employee employee, UriComponentsBuilder builder) EntityModel entityModel = this.assembler.toModel(this.employeeService.addEmployee(employee)); return ResponseEntity.created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()).body(entityModel); 你应该解决你的空指针问题,我建议在@Ventura 的那一行添加空检查 谢谢,我找到了问题,但不知道为什么。稍后我将发布编辑 2,以便您看到。无论如何,你第一个回答解决了我原来的问题。【参考方案2】:

您发布的正文实际上不是 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());

【讨论】:

您能否相应地更新您的答案并可能发布堆栈跟踪?帮助更容易 完成了,还不清楚吗?它引导我到第 64 行,addEmployee 方法的返回语句 发现了问题。不知道为什么。无论如何,您的回答帮助我解决了最初的问题,所以谢谢

以上是关于如何正确使用 MockMvc 在 Junit 中测试 post 方法?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我收到 MockMvc 和 JUnit 的错误 403?

使用 spring-data-jpa 和 MockMvc 进行 spring boot junit 测试

MockMVC的使用

如何使用 MockMvc 测试弹簧控制器方法?

Spring - 如何正确使用@Autowired 来防止控制器/ MockMvc 为空?

我需要创建方法get()和status()来创建一个带有mockmvc的测试控制器?