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

0 0
原创粉丝点击