Worker 実装のテスト

WorkManager には、Worker、ListenableWorker、ListenableWorker のバリアント(CoroutineWorker と RxWorker)をテストするための API が用意されています。

Worker のテスト

次のような Worker があるとします。

class SleepWorker(context: Context, parameters: WorkerParameters) :
    Worker(context, parameters) {

    override fun doWork(): Result {
        // Sleep on a background thread.
        Thread.sleep(1000)
        return Result.success()
    }
}

この Worker のテストには TestWorkerBuilder を使用できます。このビルダーで、ビジネス ロジックをテストするために使用する Worker のインスタンスを作成できます。

// Kotlin code uses the TestWorkerBuilder extension to build
// the Worker
@RunWith(AndroidJUnit4::class)
class SleepWorkerTest {
    private lateinit var context: Context
    private lateinit var executor: Executor

    @Before
    fun setUp() {
        context = ApplicationProvider.getApplicationContext()
        executor = Executors.newSingleThreadExecutor()
    }

    @Test
    fun testSleepWorker() {
        val worker = TestWorkerBuilder<SleepWorker>(
            context = context,
            executor = executor
        ).build()

        val result = worker.doWork()
        assertThat(result, `is`(Result.success()))
    }
}

また、TestWorkerBuilder を使用して inputData や runAttemptCount などのタグを設定し、Worker の状態を個別に確認することもできます。次の例では、SleepWorker が Worker で定義された定数でなく、入力データとしてスリープ期間を取り込む場合を想定しています。

class SleepWorker(context: Context, parameters: WorkerParameters) :
    Worker(context, parameters) {

    override fun doWork(): Result {
        // Sleep on a background thread.
        val sleepDuration = inputData.getLong(SLEEP_DURATION, 1000)
        Thread.sleep(sleepDuration)
        return Result.success()
    }

    companion object {
        const val SLEEP_DURATION = "SLEEP_DURATION"
    }
}

SleepWorkerTest で、この入力データを TestWorkerBuilder に提供して、SleepWorker のニーズを満たすことができます。

// Kotlin code uses the TestWorkerBuilder extension to build
// the Worker
@RunWith(AndroidJUnit4::class)
class SleepWorkerTest {
    private lateinit var context: Context
    private lateinit var executor: Executor

    @Before
    fun setUp() {
        context = ApplicationProvider.getApplicationContext()
        executor = Executors.newSingleThreadExecutor()
    }

    @Test
    fun testSleepWorker() {
        val worker = TestWorkerBuilder<SleepWorker>(
            context = context,
            executor = executor,
            inputData = workDataOf("SLEEP_DURATION" to 1000L)
        ).build()

        val result = worker.doWork()
        assertThat(result, `is`(Result.success()))
    }
}

TestWorkerBuilder API について詳しくは、TestListenableWorkerBuilder(TestWorkerBuilder のスーパークラス)のリファレンス ページをご覧ください。

ListenableWorker とそのバリアントのテスト

ListenableWorker またはそのバリアント(CoroutineWorker と RxWorker)をテストするには、TestListenableWorkerBuilder を使用します。TestWorkerBuilder と TestListenableWorkerBuilder の主な違いは、TestWorkerBuilder は Worker の実行に使用されるバックグラウンド Executor を指定できるのに対し、TestListenableWorkerBuilder は ListenableWorker 実装のスレッド ロジックに依存する点です。

たとえば、次のような CoroutineWorker をテストする必要があるとします。

class SleepWorker(context: Context, parameters: WorkerParameters) :
    CoroutineWorker(context, parameters) {
    override suspend fun doWork(): Result {
        delay(1000L) // milliseconds
        return Result.success()
    }
}

SleepWorker をテストするには、まず TestListenableWorkerBuilder を使用してワーカーのインスタンスを作成し、コルーチン内でその doWork 関数を呼び出します。

@RunWith(AndroidJUnit4::class)
class SleepWorkerTest {
    private lateinit var context: Context

    @Before
    fun setUp() {
        context = ApplicationProvider.getApplicationContext()
    }

    @Test
    fun testSleepWorker() {
        val worker = TestListenableWorkerBuilder<SleepWorker>(context).build()
        runBlocking {
            val result = worker.doWork()
            assertThat(result, `is`(Result.success()))
        }
    }
}

runBlocking はテストのコルーチン ビルダーとして機能し、非同期で実行されるコードは代わりに並列実行されます。

TestListenableWorkerBuilder は ListenableWorker のサブクラスを処理できるため、RxWorker 実装のテストは CoroutineWorker のテストと似ています。コルーチンではなく RxJava を使用する SleepWorker について考えてみましょう。

class SleepWorker(
    context: Context,
    parameters: WorkerParameters
) : RxWorker(context, parameters) {
    override fun createWork(): Single<Result> {
        return Single.just(Result.success())
            .delay(1000L, TimeUnit.MILLISECONDS)
    }
}

RxWorker をテストするバージョンの SleepWorkerTest は、CoroutineWorker をテストするバージョンと似ています。TestListenableWorkerBuilder を使用する点は同じです。ただし、このバージョンでは RxWorker の createWork を呼び出します。createWork は Single を返します。これを使用して、Worker の動作を確認できます。TestListenableWorkerBuilder が複雑なスレッド化を行い、Worker コードを並列実行します。

@RunWith(AndroidJUnit4::class)
class SleepWorkerTest {
    private lateinit var context: Context

    @Before
    fun setUp() {
        context = ApplicationProvider.getApplicationContext()
    }

    @Test
    fun testSleepWorker() {
        val worker = TestListenableWorkerBuilder<SleepWorker>(context).build()
        worker.createWork().subscribe { result ->
            assertThat(result, `is`(Result.success()))
        }
    }
}