RxJava version is 2.0.1
Not sure if it's a bug or my lack of knowledge. The issue I have happens when I try to return trampoline scheduler instead of io for my unit tests. An error occurs:
````
java.lang.ExceptionInInitializerError
at com.dariuszdeoniziak.charades.presenters.CategoryListPresenter.loadCategories(CategoryListPresenter.java:53)
at com.dariuszdeoniziak.charades.presenters.CategoryListPresenterTest.loadCategoriesCallsShowCategories(CategoryListPresenterTest.java:44)
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)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
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)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.NullPointerException: Scheduler Callable result can't be null
at io.reactivex.internal.functions.ObjectHelper.requireNonNull(ObjectHelper.java:39)
at io.reactivex.plugins.RxJavaPlugins.applyRequireNonNull(RxJavaPlugins.java:989)
at io.reactivex.plugins.RxJavaPlugins.initIoScheduler(RxJavaPlugins.java:213)
at io.reactivex.schedulers.Schedulers.
... 31 more
Process finished with exit code 255
````
The loadCategories code is as follows, the exception is thrown at .subscribeOn(Schedulers.io()):
Single<List<Category>> categoriesSingle = Single.fromCallable(new Callable<List<Category>>() {
@Override
public List<Category> call() throws Exception {
return modelInteractor.getCategories();
}
});
public void loadCategories() {
view.showProgressIndicator();
categoriesSingle
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<List<Category>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onSuccess(List<Category> value) {
if (value.isEmpty())
view.showEmptyList();
else
view.showCategories(value);
}
@Override
public void onError(Throwable e) {
view.showEmptyList();
}
});
}
Test just calls loadCategories method and mocks few things:
@Test
public void loadCategoriesCallsShowCategories() {
when(presenter.modelInteractor.getCategories())
.thenReturn(categories);
presenter.loadCategories();
verify(modelInteractor).getCategories();
verify(view).showProgressIndicator();
verify(view).showCategories(categories);
}
Last but not least, the place where I setInitIoSchedulerHandler is custom TestRunner:
package com.dariuszdeoniziak.charades.utils;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;
import java.util.concurrent.Callable;
import io.reactivex.Scheduler;
import io.reactivex.android.plugins.RxAndroidPlugins;
import io.reactivex.functions.Function;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.schedulers.Schedulers;
public class RxJavaTestRunner extends BlockJUnit4ClassRunner {
public RxJavaTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
RxAndroidPlugins.reset();
RxAndroidPlugins.setInitMainThreadSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
@Override
public Scheduler apply(Callable<Scheduler> schedulerCallable) throws Exception {
return Schedulers.trampoline();
}
});
RxJavaPlugins.reset();
RxJavaPlugins.setInitNewThreadSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
@Override
public Scheduler apply(Callable<Scheduler> schedulerCallable) throws Exception {
return Schedulers.trampoline();
}
});
RxJavaPlugins.setInitIoSchedulerHandler(new Function<Callable<Scheduler>, Scheduler>() {
@Override
public Scheduler apply(Callable<Scheduler> schedulerCallable) throws Exception {
return Schedulers.trampoline();
}
});
}
}
Is it possible to setInitIoSchedulerHandler as above?
Full project is available on my git: https://github.com/darekdeo/charades
I have currently replaced Schedulers.io with Schedulers.newThread on git, which works fine.
No. When you call setInitX you can't access the Schedulers from within because the callback itself executes on the Schedulers' class initialization and other Schedulers fields may not be ready. Use the plain setIoSchedulerHandler to override and later clear the custom scheduler.
It works, thank you. This was fast.
Most helpful comment
No. When you call setInitX you can't access the Schedulers from within because the callback itself executes on the Schedulers' class initialization and other Schedulers fields may not be ready. Use the plain
setIoSchedulerHandlerto override and later clear the custom scheduler.