SpringBoot中如何Mock Iterators.partition()并编写单元测试?
如何正确测试使用Guava Iterators.partition的方法
问题背景
我需要为以下processStudentData方法编写单元测试,该方法使用com.google.common.collect.Iterators.partition()实现批处理逻辑:
@Override public List<StudentsCSV> processStudentData(MappingIterator<Students> studentsMappingIterator, String studentDetails) { // ... 其他逻辑 Iterators.partition(studentsMappingIterator, BATCH_SIZE) .forEachRemaining(studentsList -> processBatch(studentDetails.getStudentId())); List<StudentsCSV> studentsCSVList = new ArrayList<>(); // ... 组装返回结果的逻辑 return studentsCSVList; }
我尝试用下面的方式Mock但没成功,想知道正确的单元测试写法:
@EnableAutoConfiguration @SpringBootTest public class AudienceDataProcessorServiceTest { @Test void test(){ List<Students> studentsList = new ArrayList<>(); Students student = new Students(); studentsList.add(student); Iterator<Students> iteratorStudent = studentsList.iterator(); Iterators iterators = mock(Iterators.class); when(iterators.partition(any(MappingIterator.class),anyInt())).thenReturn(Iterators.unmodifiableIterator(iteratorStudent)); } }
错误原因
你当前的Mock方式无效,因为Iterators.partition是静态方法,而你Mock的是Iterators类的实例对象,静态方法的调用和实例无关,所以这个Mock不会生效。
正确测试方案
方案一:构造真实的MappingIterator(推荐)
避免静态方法Mock的复杂性,直接构造真实的MappingIterator传入测试数据,更贴近真实业务场景。假设MappingIterator来自Jackson(常见场景),示例代码如下:
@ExtendWith(MockitoExtension.class) public class AudienceDataProcessorServiceTest { @InjectMocks private YourService service; // 替换成你的实际服务类 @Mock private SomeDependency dependency; // 如果processBatch依赖其他类,先Mock private static final int BATCH_SIZE = 10; // 和目标方法的BATCH_SIZE一致 @Test void testProcessStudentData() throws IOException { // 1. 构造测试用的Students列表 List<Students> testStudents = Arrays.asList(new Students(), new Students(), new Students()); // 2. 构造真实的MappingIterator ObjectMapper objectMapper = new ObjectMapper(); MappingIterator<Students> mappingIterator = objectMapper.readerFor(Students.class) .readValues(objectMapper.writeValueAsString(testStudents)); // 3. 调用目标方法 List<StudentsCSV> result = service.processStudentData(mappingIterator, "testStudentId"); // 4. 验证逻辑:比如processBatch被调用的次数(按批处理计算) int expectedBatchCalls = (int) Math.ceil((double) testStudents.size() / BATCH_SIZE); verify(dependency, times(expectedBatchCalls)).processBatch(anyString()); // 5. 验证返回结果的正确性 assertNotNull(result); // 其他断言逻辑... } }
方案二:使用Mockito静态Mock(适用于无法构造真实迭代器的场景)
如果必须MockIterators.partition,需要使用Mockito 3.4.0及以上版本的静态Mock功能,步骤如下:
- 确保依赖中包含mockito-inline(用于静态Mock)
- 编写测试代码:
@ExtendWith(MockitoExtension.class) public class AudienceDataProcessorServiceTest { @InjectMocks private YourService service; @Mock private MappingIterator<Students> mockMappingIterator; @Test void testProcessStudentDataWithStaticMock() { // 构造测试用的批处理数据 List<Students> batch1 = Arrays.asList(new Students()); List<Students> batch2 = Arrays.asList(new Students()); Iterator<List<Students>> partitionedIterator = Arrays.asList(batch1, batch2).iterator(); // 静态Mock Iterators类 try (MockedStatic<Iterators> mockedIterators = Mockito.mockStatic(Iterators.class)) { // 指定partition方法的返回值 mockedIterators.when(() -> Iterators.partition(mockMappingIterator, BATCH_SIZE)) .thenReturn(partitionedIterator); // 调用目标方法 List<StudentsCSV> result = service.processStudentData(mockMappingIterator, "testStudentId"); // 验证processBatch被调用2次(对应两个批次) verify(service, times(2)).processBatch(anyString()); // 验证返回结果 assertNotNull(result); } } }
注意事项
- 优先选择方案一,静态Mock会增加测试的耦合度,且容易引入隐藏问题
- 如果使用SpringBootTest,仅在需要容器上下文时使用,普通单元测试用
@ExtendWith(MockitoExtension.class)更轻量
内容的提问来源于stack exchange,提问作者NNFdo
相关产品推荐
相关产品推荐

