Rxjava2单元测试的异步和同步转换
来源:互联网 发布:阿里云深圳机房ip地址 编辑:程序博客网 时间:2024/06/04 01:32
自从接触到单元测试后,每次看UT的文章开头就一定会有:单元测试的必要性和好处,这里我就不累述了。
于是我渐渐入坑,加了相关群,虽然学习起来比较吃力,但是渐渐也感觉到了好处
我想,可能是单元测试让我更加理解Android的开发原则,对解耦、依赖理解更加深入。写好一个好的测试,不仅可以检测BUG,更能让你的代码更完美。
最近对RxJava做UI测试的时候,就出现了这样的情况,点击按钮触发以下事件,用Retrofit+Rxjava进行网络请求:
api.getResponse(url, param.getParameters()) .map(new Function<HttpResult, T>() { @SuppressWarnings("unchecked") @Override public T apply(HttpResult tHttpResult) throws Exception { //代码省略 }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); .subscribe(new Observer<T>() { @Override public void onSubscribe(Disposable d) { } @Override public void onNext(T value) {// } @Override public void onError(Throwable e) { } @Override public void onComplete() { } });
模拟点击按钮后RXjava的OnNext事件不触发,当时我就懵逼了很久,无奈到群里发问,虽然没有得到答案,但是遇到了与我遭遇类似的苦逼骚年。相互讨论后,他提到如何让Rxjava始终处于主线程呢?我随意的回答:用AndroidSchedulers.mainThread()直接绑定到主线程不就完了。
.subscribeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread());
咦,果然解决了他的问题,测试通过了。无心插柳柳成荫,这下终于找到问题所在了,原来是异步请求无法正确执行。
还没等开心,测试到是通过了,可尼玛我的按钮是网络请求,不会让我在主线程里面做耗时操作吧,我同意google也不同意啊。
既然这样不行,那我就分开进行呀,测试的时候同步,正式运行的时候异步不就好了吗?哈哈,我TM真是天才。
方案1、BaseSchedulerProvider
说干就干,首先我们自定义接口类BaseSchedulerProvider,让测试和正式代码个实现一下,这样来控制Scheduler。
public interface BaseSchedulerProvider { Scheduler ui(); Scheduler io();}====================================//正式final class AppSchedulerProvider implements BaseSchedulerProvider { @Override public Scheduler ui() { return AndroidSchedulers.mainThread(); } @Override public Scheduler io() { return Schedulers.io(); }}===================================//测试public class TestSchedulerProvider implements BaseSchedulerProvider { @Override public Scheduler ui() { return AndroidSchedulers.mainThread(); } @Override public Scheduler io() { return Schedulers.trampoline(); }}
再在presenter里面注入一个BaseSchedulerProvider
public LoginPresenter(LoginContract.View mView, UserRepository mUserRepository, mHttpRequest mHttpRequest, BaseSchedulerProvider mProvider) { this.mView = mView; this.mUserRepository = mUserRepository; this.mHttpRequest = mHttpRequest; this.mProvider=mProvider; }
等需要使用的时候就:
.subscribeOn(provider.io()) .observeOn(provider.ui())
然后再在测试的时候:
TestSchedulerProvider mProvider=new TestSchedulerProvider();mLoginPresenter = new LoginPresenter(mView, mUserRepository, mHttpRequest,mProvider);
这样,测试的时候自然就会调用测试的TestSchedulerProvider,完美的实现了测试和代码分离。
接下来,重点来了,看看我的测试代码:
loginActivity=Robolectric.setupActivity(LoginActivity.class);Button btn_login = (Button) loginActivity.findViewById(R.id.login_btn_login);btn_login.performClick();Intent exceptedIntent=new Intent(loginActivity, homeActivity.class);ShadowActivity shadowActivity=Shadows.shadowOf(loginActivity);Intent realIntent=shadowActivity.getNextStartedActivity(); assertEquals(exceptedIntent.getComponent(),realIntent.getComponent());
我才反应过来,我正在UI测试,mLoginPresenter 拿来干毛啊,一万只草泥马从脑海里跑过。
不过,用这种方法到是可以很好的解决presenter的单元测试,随心所欲的转换线程,我只能这么安慰自己了。
方案2、RxJavaPlugins
辛苦半天,好像线索又断了,欲哭无泪啊。毛主席教育我们,打不过就跑!我哪是那种人,哈哈。
当然,又是各种查资料,突然看到了一篇Rxjava1的测试文章:
public static void asyncToSync() { Func1<Scheduler, Scheduler> schedulerFunc = new Func1<Scheduler, Scheduler>() { @Override public Scheduler call(Scheduler scheduler) { return Schedulers.immediate(); } }; RxAndroidSchedulersHook rxAndroidSchedulersHook = new RxAndroidSchedulersHook() { @Override public Scheduler getMainThreadScheduler() { return Schedulers.immediate(); } }; RxJavaHooks.reset(); RxJavaHooks.setOnIOScheduler(schedulerFunc); RxJavaHooks.setOnComputationScheduler(schedulerFunc); RxAndroidPlugins.getInstance().reset(); RxAndroidPlugins.getInstance().registerSchedulersHook(rxAndroidSchedulersHook); }
原来Rxjava已经准备好了API给我们用,直接利用RxAndroidPlugins、RxJavaHooks这两个类,就可以控制全局,将IO线程直接转换为immediate()线程。RX1的方案这里不多说,想看详解的请点这里看原文
这RX1的方案也不适合我啊,不过既然RX1有,那RX2必然会有,跑去看了官方文档,千辛万苦(一个四级水平的孩子)的终于看到了希望的曙光:RxJavaPlugins。
/** * 单元测试的时候,利用RxJavaPlugins将io线程转换为trampoline * trampoline应该是立即执行的意思(待商榷),替代了Rx1的immediate。 */ public static void asyncToSync() { RxJavaPlugins.reset(); RxJavaPlugins.setIoSchedulerHandler(new Function<Scheduler, Scheduler>() { @Override public Scheduler apply(Scheduler scheduler) throws Exception { return Schedulers.trampoline(); } });
然后再在测试类里加这么一句:
@Before public void setUp(){ //将rx异步转同步 RxjavaFactory.asyncToSync(); }
谢天谢地,我终于看到了如下画面:
真是老泪纵横啊。
其中的trampoline()线程就是RX1的immediate线程;
Rx2里面已经找不到RxJavaHooks类了,直接使用RxJavaPlugins即可。
感谢观看,文中如果有错误的地方,大家也不要吝啬,尽情打脸吧,或者有更好的建议,请在下方留言,相互学习。
要源码的小伙伴们请点击:https://github.com/mrqatom/atomRXDT-MVP
- Rxjava2单元测试的异步和同步转换
- 同步和异步转换
- Android单元测试:测试RxJava的同步及异步操作
- ajax方法异步与同步的转换
- 同步和异步的区别
- 同步和异步的区别
- 同步和异步的区别
- 同步和异步的区别
- 同步和异步的区别
- 同步和异步的区别
- 同步和异步的理解
- 同步和异步的区别
- 同步和异步的区别
- 同步和异步的区别
- 同步和异步的区别
- Ajax的异步和同步
- 同步和异步的区别
- 同步和异步的区别
- 正则表达式30分钟入门教程
- opencv 直方图反向投影
- NYOJ-814-又见拦截导弹【最少LIS段数问题】
- 在eclispe中创建Maven web工程
- 关于碰撞检测
- Rxjava2单元测试的异步和同步转换
- MDK中问题:warning : type qualifier is meaningless on cast type return 的解决
- jdbc连接,方便自己查阅
- 【Android】去除小数点后多余的零
- 新手老手一起来!AngularJS 第二讲-深入基本语法
- hdu 2048 数塔 (最经典也是最简单的dp)
- remove-duplicates-from-sorted-list
- Sony S1512S2C加装内存
- linux目录权限与文件权限的区别