Spring Boot应用中Post与Put方法测试问题排查求助
让我们逐个分析你遇到的问题,一步步排查解决:
Post方法错误:java.lang.IllegalArgumentException: 'url' is required
这个错误主要源于两个关键问题:
1. 错误Mock了MockMvc对象
你的Post测试代码里尝试mock mockMvc.perform(),这是完全错误的用法——MockMvc是我们发起测试请求的工具,根本不需要(也不应该)去mock它。我们要mock的是EmployeeRepository这类依赖组件的方法,比如save(),来模拟数据库操作。
2. Post请求缺少必要的请求体
控制器的newEmployee方法标注了@RequestBody Employee newEmployee,但测试时你没有传入任何请求体,Spring无法解析请求,这也会触发错误。
修正后的Post测试代码如下:
@Test public void postEmployeeStatusIsOk() throws Exception { // 准备测试用的Employee对象 Employee testEmployee = getTestEmployee(); // mock repository的save方法,返回测试对象 given(this.employeeRepository.save(any(Employee.class))).willReturn(testEmployee); // 发起Post请求,传入JSON格式的请求体 mockMvc.perform(MockMvcRequestBuilders.post("/employees") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(testEmployee)) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().is2xxSuccessful()); }
另外检查你的控制器Post方法:看起来你把ResponseEntity的构建代码注释掉了,这会导致方法无法正确返回响应(甚至可能编译失败)。请取消注释这部分代码:
@PostMapping("/employees") ResponseEntity<?> newEmployee(@RequestBody Employee newEmployee) { EntityModel<Employee> entityModel = assembler.toModel(repository.save(newEmployee)); return ResponseEntity .created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()) .body(entityModel); }
Put方法错误:java.lang.NullPointerException: Cannot invoke "org.springframework.hateoas.EntityModel.getRequiredLink(...) because "entityModel" is null
这个空指针异常的根源很清晰:
1. 控制器Put方法的核心逻辑被注释
你在replaceEmployee方法里注释掉了repository.findById(id)之后的.map()和.orElseGet()逻辑,导致updatedEmployee变量为null。当调用assembler.toModel(updatedEmployee)时,传入null会返回null的EntityModel,后续调用getRequiredLink()自然会触发NPE。
首先恢复控制器Put方法的核心逻辑:
@PutMapping("/employees/{id}") ResponseEntity<?> replaceEmployee(@RequestBody Employee newEmployee, @PathVariable Long id) { Employee updatedEmployee = repository.findById(id) .map(employee -> { employee.setName(newEmployee.getName()); employee.setRole(newEmployee.getRole()); return repository.save(employee); }) .orElseGet(() -> { newEmployee.setId(id); return repository.save(newEmployee); }); EntityModel<Employee> entityModel = assembler.toModel(updatedEmployee); return ResponseEntity .created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()) .body(entityModel); }
2. 测试代码未Mock Repository的查询方法
你的Put测试中没有mockrepository.findById(1L)的返回值,导致它返回空的Optional,即使恢复了控制器逻辑,你也需要在测试中mock这个方法来返回预期的Employee对象,确保流程能正常执行。
修正后的Put测试代码:
@Test public void putEmployeeStatusIsOk() throws Exception { var employee = getTestEmployee(); employee.setId(1L); // mock repository.findById返回存在的Employee given(this.employeeRepository.findById(1L)).willReturn(Optional.of(employee)); // mock save方法返回更新后的对象 given(this.employeeRepository.save(any(Employee.class))).willReturn(employee); mockMvc.perform(MockMvcRequestBuilders.put("/employees/{id}", 1L) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(employee)) .accept(MediaType.APPLICATION_JSON)) .andExpect(status().is2xxSuccessful()); }
额外提示
- 确保你的
getTestEmployee()方法返回的是一个完整、合法的Employee对象(包含name、role等必要字段)。 - 测试HATEOAS相关的控制器时,也可以验证返回的响应中是否包含预期的链接,让测试更完整。
内容的提问来源于stack exchange,提问作者Daniel Kozak

