美文网首页
Rxjava2单元测试出现:Android RxJava2 JU

Rxjava2单元测试出现:Android RxJava2 JU

作者: 往事都随风吧 | 来源:发表于2017-08-19 15:30 被阅读365次

    最近在项目中将Rxjava升级到Rxjava2之后,对之前的P、M(项目是基于MVP结构)层进行单元测试的时候,出现了如下的问题:

    Android RxJava 2 JUnit test - getMainLooper in android.os.Looper not mocked RuntimeException

    java.lang.ExceptionInInitializerError
        at io.reactivex.android.schedulers.AndroidSchedulers$1.call(AndroidSchedulers.java:35)
        at io.reactivex.android.schedulers.AndroidSchedulers$1.call(AndroidSchedulers.java:33)
        at io.reactivex.android.plugins.RxAndroidPlugins.callRequireNonNull(RxAndroidPlugins.java:70)
        at io.reactivex.android.plugins.RxAndroidPlugins.initMainThreadScheduler(RxAndroidPlugins.java:40)
        at io.reactivex.android.schedulers.AndroidSchedulers.<clinit>(AndroidSchedulers.java:32)
        …
    Caused by: java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details.
        at android.os.Looper.getMainLooper(Looper.java)
        at io.reactivex.android.schedulers.AndroidSchedulers$MainHolder.<clinit>(AndroidSchedulers.java:29)
        ...
    java.lang.NoClassDefFoundError: Could not initialize class io.reactivex.android.schedulers.AndroidSchedulers
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
    

    出现这个问题的原因是:AndroidSchedulers.mainThread返回的是LooperScheduler的一个实例,但是LooperScheduler依赖于Android,我们在纯Java的单元测试中是无法使用它的。找到了问题的原因解决起来就容易多了。
    我们需要在单元测试运行之前,初始化RxAndroidPlugins让它返回一个我们指定的不依赖于Android的Scheduler。下面给出两种解决方案。

    方案一:

    @BeforeClass里面初始化Scheduler:

    @BeforeClass
        public static void setUpRxSchedulers(){
            final Scheduler immediate = new Scheduler() {
    
                @Override
                public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
                    return super.scheduleDirect(run, 0, unit);
                }
    
                @Override
                public Worker createWorker() {
                    return new ExecutorScheduler.ExecutorWorker(new Executor() {
                        @Override
                        public void execute(@android.support.annotation.NonNull Runnable command) {
                            command.run();
                        }
                    });
                }
            };
    
            RxJavaPlugins.setInitIoSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
                @Override
                public Scheduler apply(@NonNull Callable<Scheduler> schedulerCallable) throws Exception {
                    return immediate;
                }
            });
            RxJavaPlugins.setInitComputationSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
                @Override
                public Scheduler apply(@NonNull Callable<Scheduler> schedulerCallable) throws Exception {
                    return immediate;
                }
            });
            RxJavaPlugins.setInitNewThreadSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
                @Override
                public Scheduler apply(@NonNull Callable<Scheduler> schedulerCallable) throws Exception {
                    return immediate;
                }
            });
            RxJavaPlugins.setInitSingleSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
                @Override
                public Scheduler apply(@NonNull Callable<Scheduler> schedulerCallable) throws Exception {
                    return immediate;
                }
            });
            RxAndroidPlugins.setInitMainThreadSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
                @Override
                public Scheduler apply(@NonNull Callable<Scheduler> schedulerCallable) throws Exception {
                    return immediate;
                }
            });
        }
    

    这样做虽然可以解决问题,但是你需要在每个测试类里面都要添加这样的代码,非常麻烦,有没有简单一点的办法呢?当然是有的,我们可以自定义一个TestRule,在需要的地方直接调用就行了。

    方案二:

    自定义一个RxSchedulersOverrideRule让它实现TestRule:

    public class RxSchedulersOverrideRule implements TestRule {
    
    
        private Scheduler immediate = new Scheduler() {
    
            @Override
            public Disposable scheduleDirect(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
                return super.scheduleDirect(run, 0, unit);
            }
    
            @Override
            public Worker createWorker() {
                return new ExecutorScheduler.ExecutorWorker(new Executor() {
                    @Override
                    public void execute(@android.support.annotation.NonNull Runnable command) {
                        command.run();
                    }
                });
            }
        };
    
        @Override
        public Statement apply(final Statement base, Description description) {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    RxJavaPlugins.setInitIoSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
                        @Override
                        public Scheduler apply(@NonNull Callable<Scheduler> schedulerCallable) throws Exception {
                            return immediate;
                        }
                    });
                    RxJavaPlugins.setInitComputationSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
                        @Override
                        public Scheduler apply(@NonNull Callable<Scheduler> schedulerCallable) throws Exception {
                            return immediate;
                        }
                    });
                    RxJavaPlugins.setInitNewThreadSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
                        @Override
                        public Scheduler apply(@NonNull Callable<Scheduler> schedulerCallable) throws Exception {
                            return immediate;
                        }
                    });
                    RxJavaPlugins.setInitSingleSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
                        @Override
                        public Scheduler apply(@NonNull Callable<Scheduler> schedulerCallable) throws Exception {
                            return immediate;
                        }
                    });
                    RxAndroidPlugins.setInitMainThreadSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
                        @Override
                        public Scheduler apply(@NonNull Callable<Scheduler> schedulerCallable) throws Exception {
                            return immediate;
                        }
                    });
    
                    try {
                        base.evaluate();
                    }finally {
                        RxJavaPlugins.reset();
                        RxAndroidPlugins.reset();
                    }
                }
            };
        }
    }
    

    在需要的地方直接像下面这样调用即可:

      @ClassRule
        public static RxSchedulersOverrideRule sSchedulersOverrideRule = new RxSchedulersOverrideRule();
    

    相关文章

      网友评论

          本文标题:Rxjava2单元测试出现:Android RxJava2 JU

          本文链接:https://www.haomeiwen.com/subject/cputdxtx.html