LoginSignup
7
7

More than 5 years have passed since last update.

【Android】RxJavaの非同期処理部分をテストする

Posted at

RxJavaを使った非同期処理のコードをテストしたいときに、そのままだとエラーになったのでその対処法メモです。

結論

テスト時のみSchedulers.trampoline()を使う。

環境

RxJava 2.1.4
RxAndroid 2.0.1
Kotlin 1.1.50
Mockito 2.10.0

エラー内容

java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked.

Scheduler

まずSchedulerのinterfaceを用意

BaseSchedulerProvider.kt
interface BaseSchedulerProvider {
    fun computation(): Scheduler
    fun io(): Scheduler
    fun ui(): Scheduler
}

BaseSchedulerProviderを継承したScheduler(実装で利用)

SchedulerProvider.kt
object SchedulerProvider : BaseSchedulerProvider {
    override fun computation(): Scheduler = Schedulers.computation()
    override fun io(): Scheduler = Schedulers.io()
    override fun ui(): Scheduler = AndroidSchedulers.mainThread()
}

BaseSchedulerProviderを継承したScheduler(テストで利用)
全てSchedulers.trampoline()を返すようにしている。

ImmediateSchedulerProvider.kt
class ImmediateSchedulerProvider : BaseSchedulerProvider {
    override fun computation(): Scheduler = Schedulers.trampoline()
    override fun io(): Scheduler = Schedulers.trampoline()
    override fun ui(): Scheduler = Schedulers.trampoline()
}

実装

MVPアーキテクチャのPresenterでRxJavaでリクエストしてるところ。(説明のために簡略化してます)
mSchedulerProviderBaseSchedulerProvider型のメンバ変数。
実装時はSchedulerにSchedulerProviderを利用する。

Presenter.kt
mRepository.getList()
        .subscribeOn(mSchedulerProvider.io())
        .observeOn(mSchedulerProvider.ui())
        .subscribe({
            mView.showList(it)
        }, {
            mView.showError()
        })

テスト

Repository#getList()の戻り値をMockiot.when()でモックして、それぞれのパターンに応じてViewの特定のメソッド(今回はshowList()showError()のみ)が呼ばれているかをMockito.verify()でテストしている。
テスト時はSchedulerにImmediateSchedulerProviderを利用する。

PresenterTest.kt
@Test
fun loadListOk() {
    `when`(getList()).thenReturn(Single.just(mList))
    mPresenter.loadList()
    verify(mView).showList(mItemListWithFooter)
}

@Test
fun loadListEmpty() {
    `when`(getList()).thenReturn(Single.just(arrayListOf()))
    mPresenter.loadList()
    verify(mView).showError()
}

@Test
fun loadListError() {
    `when`(getList()).thenReturn(Single.error(Exception()))
    mPresenter.loadList()
    verify(mView).showError()
}

まとめ

Schedulers.trampoline()を使うことでRxJavaを使った非同期処理のところもテストできます。
実際に使うときはDIライブラリ(Daggerなど)でSchedulerを差し替えるようにするのが良さそうです。

補足

RxAndroidPluginregisterSchedulersHook()getMainThreadScheduler()をOverrideすることでAndroidSchedulers.mainThread()が返すSchedulerを差し替えることもできるっぽいです。

参考

googlesamples/android-architecture at todo-mvp-rxjava
https://github.com/googlesamples/android-architecture/tree/todo-mvp-rxjava/

RxAndroidPlugins (rxandroid 1.2.1 API)
https://static.javadoc.io/io.reactivex/rxandroid/1.2.1/rx/android/plugins/RxAndroidPlugins.html

unit testing - Android RxJava 2 JUnit test - getMainLooper in android.os.Looper not mocked RuntimeException - Stack Overflow
https://stackoverflow.com/questions/43356314/android-rxjava-2-junit-test-getmainlooper-in-android-os-looper-not-mocked-runt

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7