单元测试问题:空Result集合在for循环与Stream中处理行为不一致
空JOOQ Result集合遍历行为不一致问题
问题现象
同一空Result<Record>集合(data.size()=0),两种遍历方式表现完全不同:
- for循环遍历:会进入循环执行
doSomething方法 - Stream遍历:按预期跳过集合,不执行任何操作
测试基于Mockito实现,仅修改了遍历逻辑,其余测试代码未做变更。
Case 1:for循环实现
private SearchResult iterateData( Result<Record> data, ...) { for (Record record : data) { doSomething(record); }
Case 2:Stream实现
private SearchResult iterateData( Result<Record> data, ...) { data.stream().forEach(record -> doSomething(record));
测试中Mock Result对象的代码
private DefaultSearchRequestModel rowSpecificValuesTestSetup( parameters...) { DefaultSearchRequestModel searchRequest = new DefaultSearchRequestModel( Arrays.asList(....), Collections.singletonList( new SearchFilter( "test", Collections.singletonList(...))); List<Column> columns = this.searchService.filterUserAllowedColumns(...); Condition searchCondition = this.searchRepositoryMock.getSearchConditions(...); List<TableJoinMapping> joinMappings = ColumnHelper.getColumnTranslateDeviceJoinMappings( columns, searchRequest.getFilters()); Result<Record> deviceDataResultMock = Mockito.mock(Result.class); Iterator<Record> resultIterator = Mockito.mock(Iterator.class); final Table fromTableMock = Mockito.mock(Table.class); when(resultIterator.hasNext()).thenReturn(true, false); Record recordMock = Mockito.mock(Record.class); when(resultIterator.next()).thenReturn(recordMock); when(deviceDataResultMock.iterator()).thenReturn(resultIterator); when(recordMock.get(CONTRACTID)).thenReturn(contractId); ... when(this.userPermissions.getAccessPermissions()).thenReturn(searchRequest.getColumns().stream().map (name -> Column.findByName(name).getId()).collect( Collectors.toList())); when(this.searchRepositoryMock.getCurrentTable(companyId)) .thenReturn(fromTableMock); when(recordMock.get(TYPEID)).thenReturn(financialTypeId); when(this.searchRepositoryMock.getDeviceData( ArgumentMatchers.anyList(), ArgumentMatchers.anyList(), any(), any(), eq(searchRequest.getPageSize()), eq(searchRequest.getPage()), eq(searchRequest.getSortCriterias()), eq(fromTableMock), ArgumentMatchers.anyList(), eq(Optional.empty()), eq(this.dslContextMock))) .thenReturn(deviceDataResultMock); return searchRequest; }
问题原因
核心问题是Mockito对JOOQ Result接口的Mock不完整,导致不同遍历方式依赖的方法行为不一致:
- for-each循环依赖
iterator()方法,你Mock的迭代器被设置为hasNext()先返回true再返回false,因此会执行一次循环体 - Stream遍历依赖
spliterator()方法(JDK集合的Stream默认实现基于Spliterator),你未Mock该方法,Mockito返回默认的空Spliterator,因此Stream不会执行任何操作 - 你未Mock
Result.size()方法,Mockito默认返回0,导致data.size()=0的判断和迭代器行为矛盾
解决方案
方案1:补全Mock方法,保持行为一致
确保所有集合相关方法的Mock结果统一,符合空集合的行为:
// 明确设置size返回0,与空集合一致 when(deviceDataResultMock.size()).thenReturn(0); // 修改迭代器行为,直接返回false,符合空集合的迭代逻辑 when(resultIterator.hasNext()).thenReturn(false); // 可选:Mock spliterator,确保Stream行为和迭代器一致 when(deviceDataResultMock.spliterator()).thenReturn(Spliterators.emptySpliterator());
方案2:使用真实的JOOQ Result实现类(推荐)
避免Mock复杂的集合接口,直接创建真实的空Result对象,所有遍历、Stream操作都会和生产环境一致:
// 创建真实的空Result对象,传入需要的字段(如果有) Result<Record> deviceDataResultMock = DSL.using(SQLDialect.DEFAULT).newResult(CONTRACTID, TYPEID);
内容的提问来源于stack exchange,提问作者Nautilus
相关产品推荐
相关产品推荐

