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

JUnit测试Runnable逻辑遇Stream已关闭异常的解决方法

解决JUnit+Mockito测试Stream时的"source already consumed or closed"异常

核心原因

被测代码中,tasks Stream是基于try-with-resources块内的data Stream生成的。当execute()方法执行完毕后,try-with-resources会自动关闭data Stream,导致下游的tasks Stream也被关闭/消费,此时再通过ArgumentCaptor捕获的Stream调用方法就会抛出异常。

解决方案一:用Mockito Answer提前保存Runnable列表

不需要直接捕获Stream,而是在someBean.method被调用时,将Stream转换为List保存,脱离原始Stream的生命周期限制:

@InjectMocks
private App app;

@Mock
private SomeBean someBean;

private List<Runnable> capturedTasks = new ArrayList<>();

@Test
void test() {
    // 拦截someBean.method调用,将传入的Stream转为List保存
    doAnswer(invocation -> {
        Stream<Runnable> tasksStream = invocation.getArgument(0);
        capturedTasks = tasksStream.collect(Collectors.toList());
        return null;
    }).when(someBean).method(any(Stream.class));

    app.execute();

    // 执行捕获到的Runnable并验证业务逻辑
    capturedTasks.get(0).run();
    // 这里添加你的断言逻辑,比如验证某个依赖方法是否被调用
}

解决方案二:重构被测代码,抽离任务创建逻辑

如果允许修改被测代码,可以把Runnable的创建逻辑抽成独立方法,直接测试这个方法的输出:

重构后的被测类代码

@Transactional
public void execute() {
    try (Stream<Object> data = jpaRepository.fetchStream()) {
        List<Object> dataList = data.collect(Collectors.toList());
        List<Runnable> tasks = createTasks(dataList);
        someBean.method(tasks.stream());
    }
}

// 抽离独立方法,方便测试
protected List<Runnable> createTasks(List<Object> data) {
    return data.stream().map(t -> () -> {
        // 待测试的业务逻辑
    }).collect(Collectors.toList());
}

对应的测试代码

@InjectMocks
private App app;

@Test
void testCreateTasks() {
    // 构造测试数据
    List<Object> testData = Arrays.asList(new Object());

    // 调用抽离的方法获取Runnable
    List<Runnable> tasks = app.createTasks(testData);

    // 执行并验证业务逻辑
    tasks.get(0).run();
    // 添加断言逻辑
}

内容的提问来源于stack exchange,提问作者Varid Vaya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 07:17:41