LoginSignup
3
4

More than 5 years have passed since last update.

RxAndroidを利用しているソースでJVMでの単体テストをする方法

Last updated at Posted at 2017-09-10

現象

普通に作っていると AndroidSchedulers.mainThread() の部分で ExceptionInInitializerErrorになります。

何故エラーになるのか

これは、AndroidSchedulers.mainThread() によって返されるスケジューラが、Androidに依存しているためです。(Androidに依存していると何もしなければ、エミュレーターを使いAndroidTestで実行する必要があります。)
このリンク先が該当のソースになります。

解決方法

単純にテストが実行される前に、RxAndroidPlugins機構により別のSchedulerとして初期化することで、この問題を回避できます。

具体的なソース

以下kotlinのソースです。単純にPlugin機構を使って、利用するSchedulerをAndroidに依存しないようにしているだけのTestRuleを定義します。

RxImmediateSchedulerRule.kt
class RxImmediateSchedulerRule : TestRule {

    private val immediate = object : Scheduler() {
        override fun scheduleDirect(run: Runnable, delay: Long, unit: TimeUnit): Disposable {
            return super.scheduleDirect(run, 0, unit)
        }

        override fun createWorker(): Worker {
            return ExecutorScheduler.ExecutorWorker { it.run() }
        }
    }

    override fun apply(base: Statement, description: Description): Statement {
        return object : Statement() {
            @Throws(Throwable::class)
            override fun evaluate() {
                RxJavaPlugins.setInitIoSchedulerHandler({ _ -> immediate })
                RxJavaPlugins.setInitComputationSchedulerHandler({ _ -> immediate })
                RxJavaPlugins.setInitNewThreadSchedulerHandler({ _ -> immediate })
                RxJavaPlugins.setInitSingleSchedulerHandler({ _ -> immediate })
                RxAndroidPlugins.setInitMainThreadSchedulerHandler({ _ -> immediate })

                try {
                    base.evaluate()
                } finally {
                    RxJavaPlugins.reset()
                    RxAndroidPlugins.reset()
                }
            }
        }
    }
}

そして、これをテストの前に実行するだけです。ちなみに@ClassRuleを使わない理由は、こちらの記事を参照してください。

@RunWith(PowerMockRunner::class)
@PrepareForTest(Auth::class)
class LoginViewModelTest {

    @Rule
    val schedulers: RxImmediateSchedulerRule = RxImmediateSchedulerRule()

//    companion object {
//        @JvmField
//        @ClassRule
//        val schedulers: RxImmediateSchedulerRule = RxImmediateSchedulerRule()
//    }
    @Test
    fun onClickLogin() {

        val mockAuth = PowerMockito.mock(Auth::class.java)

        val target = LoginViewModel(mockAuth)

        target.mail.set("email")
        target.password.set("password")

        val result = Single.just(AuthEntity().apply {
            accessToken = "123456"
            userId = 100
        })
        PowerMockito.`when`(mockAuth.login("email", "password")).thenReturn(result)

        target.onClickLogin().run()

        Mockito.verify(mockAuth).login("email", "password")
    }
}
3
4
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
3
4