You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 23:27:08