Testowanie implementacji instancji roboczej

WorkManager udostępnia interfejsy API do testowania Worker, ListenableWorker i wariantów ListenableWorker (CoroutineWorker i RxWorker).

Pracownicy testujący

Załóżmy, że mamy Worker, który wygląda tak:

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

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

Aby przetestować Worker, możesz użyć TestWorkerBuilder. Ten kreator pomaga tworzyć instancje Worker, których można używać do testowania logiki biznesowej.

// 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 można też używać do ustawiania tagów, takich jak inputData lub runAttemptCount, aby można było zweryfikować stan instancji roboczej w izolacji. Rozważmy przykład, w którym SleepWorker przyjmuje czas trwania snu jako dane wejściowe, a nie jako stałą zdefiniowaną w skrypcie:

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"
    }
}

W SleepWorkerTest możesz przekazać te dane wejściowe do TestWorkerBuilder, aby spełnić wymagania 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()))
    }
}

Więcej informacji o interfejsie API TestWorkerBuilder znajdziesz na stronie dokumentacji TestListenableWorkerBuilder, która jest klasą nadrzędną TestWorkerBuilder.

Testowanie klasy ListenableWorker i jej wariantów

Aby przetestować ListenableWorker lub jego warianty (CoroutineWorker i RxWorker), użyj TestListenableWorkerBuilder. Główna różnica między TestWorkerBuilder a TestListenableWorkerBuilder polega na tym, że TestWorkerBuilder pozwala określić Executor w tle używany do uruchamiania Worker, podczas gdy TestListenableWorkerBuilder opiera się na logice wątków implementacji ListenableWorker.

Załóżmy na przykład, że musimy przetestować CoroutineWorker, który wygląda tak:

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

Aby przetestować SleepWorker, najpierw tworzymy instancję klasy Worker za pomocą TestListenableWorkerBuilder, a następnie wywołujemy jej funkcję doWork w ramach współprogramu.

@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 jest odpowiednim kreatorem współprogramów do testów, ponieważ każdy kod, który byłby wykonywany asynchronicznie, jest uruchamiany równolegle.

Testowanie implementacji RxWorker jest podobne do testowania CoroutineWorker, ponieważ TestListenableWorkerBuilder może obsługiwać dowolną podklasę ListenableWorker. Rozważ wersję SleepWorker, która używa RxJava zamiast współprogramów.

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

Wersja SleepWorkerTest, która testuje RxWorker, może wyglądać podobnie do wersji, która testowała CoroutineWorker. Używasz tego samego TestListenableWorkerBuilder, ale teraz wywołujesz funkcję createWork w RxWorker. createWork zwraca obiekt Single, którego możesz użyć do sprawdzenia działania workera. TestListenableWorkerBuilder obsługuje wszystkie złożone operacje związane z wątkami i wykonuje kod instancji roboczej równolegle.

@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()))
        }
    }
}