如何用JUnit测试RxUtils.applySchedulersAndErrorMapper返回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()); } }); - 接着Mock
mUserService.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

