最近在項(xiàng)目中將Rxjava升級(jí)到Rxjava2之后,對(duì)之前的P、M(項(xiàng)目是基于MVP結(jié)構(gòu))層進(jìn)行單元測(cè)試的時(shí)候,出現(xiàn)了如下的問題:
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)
出現(xiàn)這個(gè)問題的原因是:AndroidSchedulers.mainThread返回的是LooperScheduler的一個(gè)實(shí)例,但是LooperScheduler依賴于Android,我們?cè)诩僇ava的單元測(cè)試中是無法使用它的。找到了問題的原因解決起來就容易多了。
我們需要在單元測(cè)試運(yùn)行之前,初始化RxAndroidPlugins讓它返回一個(gè)我們指定的不依賴于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;
}
});
}
這樣做雖然可以解決問題,但是你需要在每個(gè)測(cè)試類里面都要添加這樣的代碼,非常麻煩,有沒有簡(jiǎn)單一點(diǎn)的辦法呢?當(dāng)然是有的,我們可以自定義一個(gè)TestRule,在需要的地方直接調(diào)用就行了。
方案二:
自定義一個(gè)RxSchedulersOverrideRule讓它實(shí)現(xiàn)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();
}
}
};
}
}
在需要的地方直接像下面這樣調(diào)用即可:
@ClassRule
public static RxSchedulersOverrideRule sSchedulersOverrideRule = new RxSchedulersOverrideRule();