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

如何用Mockito正确测试带有.retry()的RxJava 2方法?

如何正确测试带有RxJava retry()的方法

我能立刻明白你遇到的问题——这是RxJava的retry()操作符和Mockito行为之间的常见坑!让我一步步拆解原因,再给你两种适配不同场景的解决方案:

问题根源

你代码里的bar.dangerousMethod().retry(10)有个关键细节:retry()是对bar.dangerousMethod()返回的单个Completable实例进行重复订阅,而不是每次重试都重新调用bar.dangerousMethod()。

你看到的“dangerousMethod()被执行了10次”其实是误解:实际是这个方法返回的Completable被订阅了10次,而dangerousMethod()本身只被调用了1次。你的Mockito设置:

when(bar.dangerousMethod()).thenReturn(
        error(new Exception()),
        error(new Exception()),
        complete()
);

只会在**第一次调用bar.dangerousMethod()**时返回第一个错误Completable,后续的重试只是重复订阅这个同一个Completable(每次订阅都会抛出错误),所以会触发全部10次重试,最终整个流以失败告终,导致assertComplete()断言失败。

解决方案一:修改生产代码(推荐,符合重试语义)

如果你的业务逻辑是每次重试都需要重新执行dangerousMethod()(比如这个方法包含有状态操作或需要重新发起请求),那么需要用Completable.defer()包装方法调用,确保每次重试都会触发新的方法调用:

修改Foo类的execute()方法:

public Completable execute() {
    return Completable.fromAction(() -> System.out.println("first part of task"))
            // 用defer包装,每次重试都会调用bar.dangerousMethod()
            .andThen(Completable.defer(() -> bar.dangerousMethod())
                    .retry(10)
            )
            .andThen(Completable.fromAction(() -> System.out.println("sec part of task")));
}

修改后,你的原测试代码就能正常工作:前两次调用bar.dangerousMethod()返回错误,第三次返回成功,重试两次后流就会完成,断言通过。

解决方案二:不修改生产代码,调整Mockito测试逻辑

如果你不想修改生产代码,只是想测试当前的retry()行为(重复订阅同一个Completable),可以用Mockito的thenAnswer()让返回的Completable根据订阅次数动态返回错误或成功:

@Test
public void test() throws Exception {
    AtomicInteger subscribeCount = new AtomicInteger(0);
    
    when(bar.dangerousMethod()).thenAnswer(invocation -> {
        int count = subscribeCount.incrementAndGet();
        // 前两次订阅返回错误,第三次返回成功
        if (count <= 2) {
            return Completable.error(new Exception("Retry failed"));
        } else {
            return Completable.complete();
        }
    });

    foo.execute()
            .test()
            .assertComplete();
}

这里用AtomicInteger跟踪订阅次数,Mock返回的Completable会根据订阅次数决定行为:前两次订阅抛出错误,第三次订阅成功,retry(10)会在第三次订阅时成功,整个流完成,断言通过。

额外验证(可选)

你可以添加验证来确认行为是否符合预期:

// 方案一(修改生产代码):验证bar.dangerousMethod()被调用3次
verify(bar, times(3)).dangerousMethod();

// 方案二(不修改生产代码):验证bar.dangerousMethod()只被调用1次
verify(bar, times(1)).dangerousMethod();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:47:21