Mock实现PUT功能的用户更新控制器是否正确?Mockito/Junit新手咨询
刚接触Mockito和JUnit很正常,咱们一步步拆解你的测试代码里的问题,再给出修正方案:
你的测试代码存在的核心问题
1. Mock返回类型与业务方法不匹配
你的业务层updateUser方法返回的是boolean,但测试里Mock时却返回了Optional<User>,这会直接触发类型转换异常——Mockito要求Mock方法的返回值必须和原方法的返回类型严格一致。
2. PUT请求未携带更新参数的请求体
你要更新邮箱、密码和年龄,但mockMvc.perform里没有传递JSON格式的请求体,控制器根本拿不到这些更新数据,导致后续调用userService.updateUser时参数错误,断言必然失败。
3. 多余且无效的Address断言
业务层的updateUser方法只接收username、email、password、age四个参数,完全不涉及address字段,你最后断言addressCaptor.getValue()完全是多余的,若未声明addressCaptor还会直接报错。
4. ArgumentCaptor未声明
测试里用到了userCaptor、emailCaptor等参数捕获器,但没有提前声明它们。比如需要通过ArgumentCaptor<String> userCaptor = ArgumentCaptor.forClass(String.class);或者@Captor注解来定义捕获器,否则无法正常使用。
修正后的测试代码示例
假设你用的是JUnit 5 + Mockito,修正后的代码如下:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) // 启用Mockito注解支持 public class UserControllerTest { @Mock private UserService userService; @Mock private MockMvc mockMvc; private static final String BASE_URL = "/api"; // 用@Captor简化捕获器声明 @Captor private ArgumentCaptor<String> userCaptor; @Captor private ArgumentCaptor<String> emailCaptor; @Captor private ArgumentCaptor<String> passwordCaptor; @Captor private ArgumentCaptor<Integer> ageCaptor; @Test public void testUpdateUserController() throws Exception{ String username = "User"; String email = "user@email.com"; String password = "password123"; int age = 90; // 修正Mock:返回业务层要求的boolean类型 when(userService.updateUser(username, email, password, age)).thenReturn(true); // 构造JSON请求体,携带要更新的参数 String requestBody = "{\"email\":\"" + email + "\",\"password\":\"" + password + "\",\"age\":" + age + "}"; mockMvc.perform(MockMvcRequestBuilders.put(BASE_URL + "/users/{username}", username) .contentType(MediaType.APPLICATION_JSON) // 推荐使用标准的APPLICATION_JSON .content(requestBody)) // 传递请求体 .andExpect(MockMvcResultMatchers.status().isOk()); // 验证userService的updateUser方法被调用1次,并捕获参数 verify(userService, times(1)) .updateUser(userCaptor.capture(), emailCaptor.capture(), passwordCaptor.capture(), ageCaptor.capture()); // 断言捕获的参数与预期一致 assertEquals(username, userCaptor.getValue()); assertEquals(email, emailCaptor.getValue()); assertEquals(password, passwordCaptor.getValue()); assertEquals(Integer.valueOf(age), ageCaptor.getValue()); } }
额外的控制器层注意事项
确保你的控制器正确接收请求体参数,建议用一个DTO来封装仅需更新的字段(避免暴露完整User实体):
// 定义仅包含更新字段的DTO public class UserUpdateDto { private String email; private String password; private Integer age; // getter和setter方法 } // 控制器的PUT接口 @PutMapping("/users/{username}") public ResponseEntity<Void> updateUser(@PathVariable String username, @RequestBody UserUpdateDto updateDto) { boolean updateSuccess = userService.updateUser(username, updateDto.getEmail(), updateDto.getPassword(), updateDto.getAge()); return updateSuccess ? ResponseEntity.ok().build() : ResponseEntity.notFound().build(); }
内容的提问来源于stack exchange,提问作者Herindra Setiawan
相关产品推荐
相关产品推荐

