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

如何用JUnit测试RxUtils.applySchedulersAndErrorMapper返回null的请求场景?

可行的JUnit测试方案针对RxUtils返回null的场景

我懂你现在的麻烦——想给onContinueClicked这个方法写JUnit测试,但RxUtils.applySchedulersAndErrorMapper()居然返回null,直接跑测试肯定会触发空指针异常对吧?别慌,这里有几个实用的方案能帮你解决这个问题:

方案1:用PowerMock静态Mock掉RxUtils的方法

因为applySchedulersAndErrorMapper()是静态方法,返回null的话,咱们直接Mock它让它返回一个有效的ObservableTransformer就行,这样订阅逻辑就能正常走下去了。

具体步骤:

  • 先给你的测试类加上必要的PowerMock注解:
    @RunWith(PowerMockRunner.class)
    @PrepareForTest(RxUtils.class)
    public class YourPresenterTest {
        // 你的测试代码
    }
    
  • 在测试方法里,先静态Mock RxUtils,让它返回一个适合测试的Transformer(用trampoline调度器,避免线程问题):
    PowerMockito.mockStatic(RxUtils.class);
    when(RxUtils.applySchedulersAndErrorMapper()).thenReturn(new ObservableTransformer<Object, Object>() {
        @Override
        public ObservableSource<Object> apply(Observable<Object> upstream) {
            return upstream.subscribeOn(Schedulers.trampoline())
                          .observeOn(Schedulers.trampoline());
        }
    });
    
  • 接着MockmUserService.checkUserApprovedStatus(phoneNumber),让它返回测试用的Observable,比如成功场景返回Observable.just(mockResponse),错误场景返回Observable.error(new RuntimeException("测试错误"))
  • 最后调用presenter.onContinueClicked(testPhoneNumber),验证mView.showLoading()是否被调用,以及订阅的成功/错误回调逻辑是否符合预期

方案2:重构RxUtils,让它更易测试

如果不想用PowerMock(毕竟静态Mock后期维护成本有点高),可以给RxUtils做个小重构,让它的依赖可配置,这样测试时就能控制它的行为了:

比如修改RxUtils的代码:

object RxUtils {
    // 把调度器改成可配置的,测试时替换成trampoline
    private var subscribeScheduler: Scheduler = Schedulers.io()
    private var observeScheduler: Scheduler = AndroidSchedulers.mainThread()

    fun <T> applySchedulersAndErrorMapper(): ObservableTransformer<T, T>? {
        // 这里可以加个判断,如果是测试环境直接返回有效Transformer
        return ObservableTransformer { upstream ->
            upstream.subscribeOn(subscribeScheduler)
                    .observeOn(observeScheduler)
                    .onErrorResumeNext { error ->
                        // 你的错误映射逻辑
                        Observable.error(mapError(error))
                    }
        }
    }

    // 测试专用方法,设置测试用调度器
    fun setTestSchedulers() {
        subscribeScheduler = Schedulers.trampoline()
        observeScheduler = Schedulers.trampoline()
    }
}

然后在测试类的@Before方法里提前初始化:

@Before
fun setup() {
    RxUtils.setTestSchedulers()
    // 如果applySchedulersAndErrorMapper()是因为某些配置才返回null,在测试环境下确保它返回有效的Transformer
}

这样一来,测试时就不会因为Android主线程调度器不可用导致方法返回null,同时也能正常测试调度和错误映射逻辑。

方案3:直接跳过compose调用(适合快速测试业务逻辑)

如果你的测试重点是checkUserApprovedStatus的订阅逻辑,而非applySchedulersAndErrorMapper的转换逻辑,那可以用Mockito的spy或者Answer来拦截Observable,跳过compose步骤:

// Mock mUserService的checkUserApprovedStatus方法,返回原始Observable不执行compose
doAnswer { invocation ->
    val originalObservable = invocation.callRealMethod() as Observable<Response>
    originalObservable // 直接返回,不经过compose
}.`when`(mUserService).checkUserApprovedStatus(anyString())

不过这个方案更适合快速验证业务流程,如果你需要测试错误映射或者调度逻辑,还是优先选前两个方案。

最后提醒一句:JUnit环境下不能用AndroidSchedulers.mainThread(),所以不管用哪个方案,都要把调度器换成Schedulers.trampoline()或者Schedulers.immediate(),避免测试卡住或者报错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:46:55