使用API为我的webapp编写一些测试,测试控制器方法。控制器使用我想要模拟的服务方法。 测试代码:
@Mock
private UserService userService;
@Mock
private PasswordEncoder passwordEncoder;
@InjectMocks
private UserApi userApi;
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(userApi).build();
@Test
public void addUser() throws Exception {
Role role = new Role(1L, "ROLE_USER");
User user = new User (null, "Test user", "Test password", true, role);
when(roleService.findByName(role.getName())).thenReturn(role);
when(passwordEncoder.encode(user.getPassword())).thenReturn("Encoded password");
when(userService.save(user)).thenReturn(1L);
mockMvc.perform(MockMvcRequestBuilders.post("/user").content(user.toJson())).andDo(print())
.andExpect(status().isOk())
.andExpect(content().contentType("application/json"))
.andExpect(content().string(user.toJson()));
}
当UserService.save(用户)调用它时,如果为null,则将user的id属性设置为某个唯一的Long值。我可以设置Mock of UserService.save(用户)来更改已保存用户的ID,如同真正的save()吗? 与PasswordEncoder相同,当调用PasswordEncoder.encode(string)时,它将字符串值更改为编码值,如何说PasswordEncoder的Mock也一样?
答案 0 :(得分:1)
假设你是单元测试而不是功能/集成测试,如果你嘲笑UserService
和User
那么你不关心他们的行为,你就是在“嘲笑”它。这意味着您可以告诉它返回先前设置的User
或模拟User
。
即。只需在创建User
对象
答案 1 :(得分:1)
想要这是一种测试气味,可能意味着你测试的太多了;因此,我不会推荐它。您的单元测试是测试Spring控制器,它不测试UserService
或PasswordEncoder
,并且Spring控制器的责任不是确保用户的ID已更改,或者密码已编码。您可以验证是否已调用userService
和passwordEncoder
,但就测试控制器而言应该这样做。
听起来你真正想做的不是模拟UserService
和PasswordEncoder
,而是在测试数据库上执行与真实类的端到端测试,例如。您可以从控制器开始POST新用户,然后从测试数据库中取回它,并验证您拥有的结果是否正确。在这种情况下,您不是对Spring控制器进行单元测试,而是整个真实链。
也就是说,您可以使用thenAnswer
代替thenReturn
设置自定义答案来执行您要求的内容:
when(userService.save(user)).thenAnswer(new Answer<Long>() {
@Override
public Long answer(InvocationOnMock invocation) {
user.setId(1L);
return 1L;
}
});
在模拟上调用方法时,将调用此自定义答案。其结果将是模拟方法返回的结果。在这里,它还在返回之前设置用户的id。这仍然是非常可怕的,并且进行适当的集成测试将更加可取。