Spring Boot Service单元测试:如何验证含事务方法的集合调用?
问题描述
我有如下UserServiceImpl服务类:
@Service @Transactional public class UserServiceImpl implements UserDetailsService { ... public void addRoleToUser(String username, String name) { User user = this.userRepository.findByUsername(username); Role role = this.roleRepository.findByName(name); user.getRoles().add(role); } }
以及对应的测试类:
public class UserServiceImplTest { @Mock private UserRepository userRepository; @Mock private RoleRepository roleRepository; private AutoCloseable autoCloseable; private UserServiceImpl userServiceImpl; @BeforeEach void setUp(){ autoCloseable = MockitoAnnotations.openMocks(this); userServiceImpl = new UserServiceImpl(userRepository, roleRepository); } @AfterEach void tearDown() throws Exception{ autoCloseable.close(); } @Test void testAddRoleToUser() { ... // user and role def. here userServiceImpl.addRoleToUser(username, roleName); // what should be here? } }
请问如何在单元测试中验证user.getRoles().add(role);是否被调用?使用Mockito和ArgumentCaptor是否足够?
解决方案
完全可以用Mockito实现这个验证,ArgumentCaptor或直接Mock User对象都能满足需求,两种方式各有适用场景:
方式一:Mock User对象直接验证集合方法调用
如果把User对象设为Mockito的Mock实例,你可以直接验证getRoles().add()的调用行为,步骤如下:
@Test void testAddRoleToUser() { // 1. 定义测试数据 String username = "testUser"; String roleName = "ROLE_ADMIN"; // 2. 创建Mock对象 User mockUser = Mockito.mock(User.class); Role mockRole = Mockito.mock(Role.class); Collection<Role> mockRoles = Mockito.mock(Collection.class); // 3. 配置Mock行为 Mockito.when(userRepository.findByUsername(username)).thenReturn(mockUser); Mockito.when(roleRepository.findByName(roleName)).thenReturn(mockRole); Mockito.when(mockUser.getRoles()).thenReturn(mockRoles); // 4. 执行测试方法 userServiceImpl.addRoleToUser(username, roleName); // 5. 验证调用:确认add方法被传入mockRole调用过一次 Mockito.verify(mockRoles, Mockito.times(1)).add(mockRole); }
这种方式直接针对Mock集合对象验证方法调用,逻辑清晰,适合只需要确认“方法被调用过”的场景。
方式二:用真实对象验证集合内容变化
如果User是真实实例(非Mock),可以直接检查它的roles集合是否确实添加了目标Role,步骤如下:
@Test void testAddRoleToUser() { // 1. 定义测试数据 String username = "testUser"; String roleName = "ROLE_ADMIN"; // 2. 创建真实对象并初始化集合 User realUser = new User(); realUser.setUsername(username); realUser.setRoles(new ArrayList<>()); Role targetRole = new Role(); targetRole.setName(roleName); // 3. 配置Mock行为 Mockito.when(userRepository.findByUsername(username)).thenReturn(realUser); Mockito.when(roleRepository.findByName(roleName)).thenReturn(targetRole); // 4. 执行测试方法 userServiceImpl.addRoleToUser(username, roleName); // 5. 验证集合内容 Assertions.assertTrue(realUser.getRoles().contains(targetRole)); Assertions.assertEquals(1, realUser.getRoles().size()); }
如果需要验证的是“集合确实被修改并包含正确元素”,而非仅方法被调用,这种方式更准确。ArgumentCaptor在这里并非必须,但如果是复杂场景(比如无法直接控制Repository返回对象),也可以用它捕获查询到的User:
// ArgumentCaptor捕获User的示例 ArgumentCaptor<String> usernameCaptor = ArgumentCaptor.forClass(String.class); Mockito.verify(userRepository).findByUsername(usernameCaptor.capture()); Assertions.assertEquals(username, usernameCaptor.getValue()); User capturedUser = userRepository.findByUsername(usernameCaptor.getValue()); Assertions.assertTrue(capturedUser.getRoles().contains(targetRole));
总结
- 仅需验证
add()方法被调用:用Mock User+Mock集合的方式最直接; - 需要验证集合实际变化:用真实User对象+断言集合内容的方式更准确;
- ArgumentCaptor并非必须,但可辅助确认Repository调用参数,或在无法直接控制返回对象的场景下捕获目标对象。
内容的提问来源于stack exchange,提问作者StudentWhoCurious
相关产品推荐
相关产品推荐

