如何用Mockito测试Stream中多次调用内部方法的列表转换方法?
问题描述
我需要验证fromLeaveListToAdminHrMngLeaveList方法的返回值是否与预期响应列表一致,但该方法通过Stream多次调用内部方法fromLeaveToAdminHrMngLeave,不清楚如何Mock该内部方法的行为。相关代码如下:
待测试方法
@Override public List<LeaveResponseEntity> fromLeaveListToAdminHrMngLeaveList(Set<Leave> leaveList) { return leaveList.stream().map(this::fromLeaveToAdminHrMngLeave).toList(); }
内部转换方法
@Override public LeaveResponseEntity fromLeaveToAdminHrMngLeave(Leave leave) { var response= AdminHrMngLeaveResponse.builder() .leaveId(leave.getLeaveId()) .leaveType(leave.getLeaveType()) .leaveStarts(leave.getLeaveStarts()) .leaveEnds(leave.getLeaveEnds()) .approvedBy(null) .approvedOn(leave.getApprovedOn()) .approved(leave.isApproved()) .requestedBy(leave.getRequestedBy().getEmail()) .build(); try{ response.setApprovedBy(userRepo.findById(leave.getApprovedBy()).orElseThrow().getEmail()); }catch (NoSuchElementException e){ response.setApprovedBy(null); } return response; }
当前测试代码
public class LeaveConvertersTest { @InjectMocks private EntityResponseLeaveConverterImpl leaveConverter; @Mock private UserRepository userRepo; @BeforeEach void setUp(){ MockitoAnnotations.openMocks(this); leaveConverter = new EntityResponseLeaveConverterImpl(userRepo); } @Test @DisplayName("Should convert a leave entity list to adminHrMng response list") void shouldConvertALeaveEntityListToAdminHrMngResponseList() { var leaveSet = Instancio.createSet(Leave.class); List<AdminHrMngLeaveResponse> expectedResponse = leaveSet.stream().map(leave -> AdminHrMngLeaveResponse.builder() .leaveId(leave.getLeaveId()) .leaveType(leave.getLeaveType()) .leaveStarts(leave.getLeaveStarts()) .leaveEnds(leave.getLeaveEnds()) .approvedBy(leave.getApprovedBy().toString()) .approvedOn(leave.getApprovedOn()) .approved(leave.isApproved()) .requestedBy(leave.getRequestedBy().getEmail()) .build() ) .toList(); when(leaveConverter.fromLeaveToAdminHrMngLeave(???)).thenReturn(???); var response = leaveConverter.fromLeaveListToAdminHrMngLeaveList(leaveSet); Assertions.assertEquals(expectedResponse,response); } }
解决方案
不需要Mock内部的fromLeaveToAdminHrMngLeave方法,这个列表转换方法的核心逻辑就是遍历集合并调用单个转换方法,测试真实行为更有意义;且内部方法依赖userRepo,只需MockuserRepo的行为就能控制内部方法输出,进而验证整个列表的转换结果。
具体调整步骤如下:
1. 修正测试类初始化
使用@InjectMocks时,无需手动创建leaveConverter实例,MockitoAnnotations.openMocks(this)会自动将Mock的userRepo注入进去,删除手动实例化代码:
@BeforeEach void setUp(){ MockitoAnnotations.openMocks(this); // 移除这行:leaveConverter = new EntityResponseLeaveConverterImpl(userRepo); }
2. Mock UserRepository的行为
遍历生成的leaveSet,为每个leave的approvedBy字段模拟userRepo.findById的返回结果,覆盖用户存在和不存在的场景:
// 模拟每个leave对应的审批用户存在的情况 for (Leave leave : leaveSet) { Long approvedById = leave.getApprovedBy(); User mockUser = new User(); mockUser.setEmail("test-user-" + approvedById + "@example.com"); when(userRepo.findById(approvedById)).thenReturn(Optional.of(mockUser)); // 若要测试用户不存在的场景,可单独设置某个leave的Mock: // if (approvedById == 1L) { // when(userRepo.findById(approvedById)).thenReturn(Optional.empty()); // } }
3. 调整预期响应的构建
预期响应需和内部转换方法逻辑完全一致,比如用户存在时取邮箱,不存在时设为null:
List<AdminHrMngLeaveResponse> expectedResponse = leaveSet.stream().map(leave -> { String approvedByEmail = "test-user-" + leave.getApprovedBy() + "@example.com"; // 如果模拟了用户不存在的场景,这里要对应设置为null // if (leave.getApprovedBy() == 1L) { // approvedByEmail = null; // } return AdminHrMngLeaveResponse.builder() .leaveId(leave.getLeaveId()) .leaveType(leave.getLeaveType()) .leaveStarts(leave.getLeaveStarts()) .leaveEnds(leave.getLeaveEnds()) .approvedBy(approvedByEmail) .approvedOn(leave.getApprovedOn()) .approved(leave.isApproved()) .requestedBy(leave.getRequestedBy().getEmail()) .build(); }).toList();
4. 移除无效Mock代码
删除针对fromLeaveToAdminHrMngLeave的Mock代码,直接调用真实方法测试:
// 移除这行:when(leaveConverter.fromLeaveToAdminHrMngLeave(???)).thenReturn(???); var response = leaveConverter.fromLeaveListToAdminHrMngLeaveList(leaveSet); Assertions.assertEquals(expectedResponse, response);
完整修改后的测试代码
public class LeaveConvertersTest { @InjectMocks private EntityResponseLeaveConverterImpl leaveConverter; @Mock private UserRepository userRepo; @BeforeEach void setUp(){ MockitoAnnotations.openMocks(this); } @Test @DisplayName("Should convert a leave entity list to adminHrMng response list") void shouldConvertALeaveEntityListToAdminHrMngResponseList() { var leaveSet = Instancio.createSet(Leave.class); // Mock UserRepository行为 for (Leave leave : leaveSet) { Long approvedById = leave.getApprovedBy(); User mockUser = new User(); mockUser.setEmail("test-user-" + approvedById + "@example.com"); when(userRepo.findById(approvedById)).thenReturn(Optional.of(mockUser)); } // 构建预期响应列表 List<AdminHrMngLeaveResponse> expectedResponse = leaveSet.stream().map(leave -> AdminHrMngLeaveResponse.builder() .leaveId(leave.getLeaveId()) .leaveType(leave.getLeaveType()) .leaveStarts(leave.getLeaveStarts()) .leaveEnds(leave.getLeaveEnds()) .approvedBy("test-user-" + leave.getApprovedBy() + "@example.com") .approvedOn(leave.getApprovedOn()) .approved(leave.isApproved()) .requestedBy(leave.getRequestedBy().getEmail()) .build() ) .toList(); // 调用待测试方法 var response = leaveConverter.fromLeaveListToAdminHrMngLeaveList(leaveSet); // 断言结果一致 Assertions.assertEquals(expectedResponse, response); } }
内容的提问来源于stack exchange,提问作者user14800584
相关产品推荐
相关产品推荐

