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

单元测试问题:空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不完整,导致不同遍历方式依赖的方法行为不一致:

  1. for-each循环依赖iterator()方法,你Mock的迭代器被设置为hasNext()先返回true再返回false,因此会执行一次循环体
  2. Stream遍历依赖spliterator()方法(JDK集合的Stream默认实现基于Spliterator),你未Mock该方法,Mockito返回默认的空Spliterator,因此Stream不会执行任何操作
  3. 你未MockResult.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 15:09:20