くうと徒然なるままに

モバイルアプリを作りながらバックエンドも作っています。

Spek/Kotlin を使っている時に LiveData を利用した Unit Test を記述する

Live Data を使ったコードのテストを記述する場合にはメインスレッドで実行しなければいけない

Live data をUnit Test であれこれする時には、 MainThread で実行しなければいけないという制限が問題になります。 具体的には、 LiveData の setValue メソッドを呼び出す時には ArchTaskExecutor.getInstance().isMainThread() を呼び出して現在がメインスレッドで実行されているかの確認を行い、メインスレッドであれば実行するみたいなことをしてます。

Unit Test で実行する場合には、メインスレッドで実行されないため、問題になります。

普通にテスト記述していく場合には、 下記のようにaac の core-testing library に存在する InstantTaskExecutorRule() をTaskExecuter に当てはめてあげれば実行できるかと認識しています。

    @Rule
    @JvmField
    val instantTaskExecutorRule = InstantTaskExecutorRule()

Spek で実行する場合の問題点

Spek で Unit Test を記述していく場合の問題点として、「Spek ではSpekのコンストラクタに Lambda を渡し、その中に DSL 等を含みテストコードを記述していく」というものが存在します。

object MyTest: Spek({
    group("a group") {
        test("a test") {
            ...
        }

        group("a nested group") {
            test("another test") {
                ...
            }
        }
    }
})

spekframework.org

仮に書いたとしてもこんな感じで表示される.

f:id:kuxumarin:20190318084407p:plain

Spek で LiveData を利用した時にメインスレッド以外でもテストを実行できるようにする方法

上記で記したように ArchTaskExecutor というクラスを置き換えることで(?)普通のJUnit でLive Data なクラスを実行する時には実現してます。

実は、 ArchTestExecutor には、Executor を独自のものに置き換えれるメソッドが生えています。ということでそれを使い実行していけば大丈夫です。 具体的には以下のようなコードで大丈夫です。

    beforeEachTest {
        ArchTaskExecutor
            .getInstance()
            .setDelegate(object : TaskExecutor() {
                override fun executeOnDiskIO(runnable: Runnable) {
                    runnable.run()
                }

                override fun isMainThread(): Boolean {
                    return true
                }

                override fun postToMainThread(runnable: Runnable) {
                    runnable.run()
                }

            })
    }

    afterEachTest {
        ArchTaskExecutor.getInstance().setDelegate(null)
    }