RxJava/RxAndroid의 비동기 테스트에서 "java.lang.Ex ceptionInitializaerError"가 발생했을 때...

java.lang.ExceptionInInitializerError


RxJava/RxAndroid의 비동기식 테스트를 쓸 때 "java.lang.Ex ceptionInitializerror"오류가 발생했습니다.
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)
  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)
    ... 41 more

까닭


잘 썼다고 생각하는 부분.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())에서.AndroidSchedulers.mainThread() 안드로이드가 의존하는 반을 이용했기 때문이다.
참조
https://stackoverflow.com/questions/43356314/android-rxjava-2-junit-test-getmainlooper-in-android-os-looper-not-mocked-runt
https://qiita.com/ko2ic/items/397fd147d4f3d0bdca09

대책


RxJavaPlagins/RxAndroid Plugins를 사용합니다.
테스트@Before 등에 다음 처리를 쓰면Scheduller를 지정하여 오류를 피할 수 있습니다.
val immediate = object : Scheduler() {
    override fun scheduleDirect(run: Runnable, delay: Long, unit: TimeUnit): Disposable {
        return super.scheduleDirect(run, 0, unit)
    }

    override fun createWorker(): Scheduler.Worker {
        return ExecutorScheduler.ExecutorWorker(Executor { it.run() })
    }
}
RxJavaPlugins.setInitIoSchedulerHandler { immediate }
RxJavaPlugins.setInitComputationSchedulerHandler { immediate }
RxJavaPlugins.setInitNewThreadSchedulerHandler { immediate }
RxJavaPlugins.setInitSingleSchedulerHandler { immediate }
RxAndroidPlugins.setInitMainThreadSchedulerHandler { immediate }

진일보한 문제


이러면 괜찮을 줄 알았는데 또 새로운 문제가 생겼군..
비동기 처리에 성공한 후에 비동기 처리를 계속하면 두 번째 비동기 처리는 영원히 끝나지 않는다.
(이 대책과 무관한 부분도 원인의 가능성을 부정할 수 없다...)

대책(2)


시험 때만.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())변경.subscribeOn(Schedulers.trampoline()).observeOn(Schedulers.trampoline()).
Scheduller를 변경했으면 좋겠다는 생각입니다.
Drid Kaigi의 응용 프로그램을 보면 Repository는 프로젝트SchedulerProvider를 하고 테스트 시 주입TestSchedulerProvider을 한다.
https://github.com/DroidKaigi/conference-app-2018/blob/master/app/src/main/java/io/github/droidkaigi/confsched2018/data/repository/ContributorDataRepository.kt#L19
확실히 RxJavaPlagins/RxAndroid Plugins에서 변경하는 것은 약간의 흑마술감이 있기 때문에 DI로 바꾸는 것이 좋을 것 같다고 생각합니다.

결론


· Scheduller를 변경해야 합니다.
· DI로 Scheduller를 주입하여 테스트 시Schedulers.trampoline()를 사용하는 것이 좋습니다.

좋은 웹페이지 즐겨찾기