如何在SpringBootTest中用JUnit和Mockito模拟List场景下的ModelMapper?
解决JUnit+Mockito中ModelMapper处理列表时返回值被覆盖的问题
问题场景
你通过ModelMapper将数据库查询到的EmployeeEntity列表映射为EmployeeInfoPojo列表返回:
employeeList = employeeRepository.findAll(); employeeListPojos = employeeList.stream().map((emp) -> modelMapper.map(emp, EmployeeInfoPojo.class)) .collect(Collectors.toList());
但在测试时,模拟ModelMapper后,返回的列表中所有条目都被最后一个convertedPojo2覆盖,问题出在这段循环存根的代码:
for(EmployeeInfoPojo co : employeesResult){ BDDMockito.when(modelMapper.map(any(),any())) .thenReturn(co); }
问题原因
Mockito的when(...).thenReturn(...)是覆盖式存根,每次调用都会替换之前的存根配置。循环结束后,modelMapper.map(any(),any())的返回值会被设置为最后一个循环的convertedPojo2,因此所有调用都会返回这个对象。
解决方案
方案1:按实体参数精准匹配返回值
针对每个EmployeeEntity实例,指定对应的EmployeeInfoPojo返回值,确保映射关系准确:
// 给第一个实体指定对应的DTO BDDMockito.when(modelMapper.map(eq(employee), eq(EmployeeInfoPojo.class))) .thenReturn(convertedPojo1); // 给第二个实体指定对应的DTO BDDMockito.when(modelMapper.map(eq(employee2), eq(EmployeeInfoPojo.class))) .thenReturn(convertedPojo2);
这种方式依赖实体的equals方法(若用Lombok Builder生成,默认会实现该方法),能保证每个实体映射到正确的DTO。
方案2:按调用顺序链式返回
如果stream处理实体的顺序固定(和列表添加顺序一致),可以用链式thenReturn指定多次调用的返回值:
BDDMockito.when(modelMapper.map(any(), eq(EmployeeInfoPojo.class))) .thenReturn(convertedPojo1, convertedPojo2);
第一次调用modelMapper.map返回convertedPojo1,第二次返回convertedPojo2,刚好匹配stream处理两个实体的顺序。
方案3:自定义Answer逻辑动态返回
如果列表较长,不想写多个存根,可以用Answer接口自定义映射逻辑,根据传入的实体返回对应DTO:
BDDMockito.when(modelMapper.map(any(EmployeeEntity.class), eq(EmployeeInfoPojo.class))) .thenAnswer(invocation -> { EmployeeEntity inputEmp = invocation.getArgument(0); // 根据实体ID匹配对应的DTO if ("1L".equals(inputEmp.getId())) { return convertedPojo1; } else if ("2L".equals(inputEmp.getId())) { return convertedPojo2; } // 处理未匹配的情况,可返回null或抛异常 throw new IllegalArgumentException("Unknown employee: " + inputEmp.getId()); });
内容的提问来源于stack exchange,提问作者Sanket Gupta
相关产品推荐
相关产品推荐

