Тестирование реализации Worker

WorkManager предоставляет API для тестирования Worker, ListenableWorker и вариантов ListenableWorker (CoroutineWorker и RxWorker).

Тестирование рабочих ВМ

Допустим, у нас есть 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, чтобы вы могли проверять состояние исполнителя изолированно. Рассмотрим пример, в котором SleepWorker принимает продолжительность сна в качестве входных данных, а не константы, определенной в исполнителе:

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

Дополнительную информацию об API TestWorkerBuilder можно найти на справочной странице TestListenableWorkerBuilder – надкласса TestWorkerBuilder.

Тестирование ListenableWorker и его вариантов

Чтобы протестировать ListenableWorker или его варианты (CoroutineWorker и RxWorker), используйте TestListenableWorkerBuilder. Основное различие между TestWorkerBuilder и TestListenableWorkerBuilder заключается в том, что TestWorkerBuilder позволяет указать фоновый Executor, используемый для запуска Worker, а TestListenableWorkerBuilder полагается на логику потоков реализации ListenableWorker.

Например, нам нужно протестировать CoroutineWorker, которая выглядит следующим образом:

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

Чтобы протестировать SleepWorker, мы сначала создаем экземпляр Worker с помощью 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 подходит для тестов в качестве построителя сопрограмм, так как любой асинхронный код будет выполняться параллельно.

Тестирование реализации RxWorker похоже на тестирование CoroutineWorker, поскольку TestListenableWorkerBuilder может обрабатывать любой подкласс ListenableWorker. Рассмотрите версию SleepWorker, в которой вместо сопрограмм используется RxJava.

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

Версия SleepWorkerTest, в которой тестируется RxWorker, может быть похожа на версию, в которой тестируется CoroutineWorker. Вы используете тот же TestListenableWorkerBuilder, но теперь вызываете функцию createWork из RxWorker. createWork возвращает Single, который можно использовать для проверки поведения воркера. TestListenableWorkerBuilder обрабатывает любые сложности с потоками и параллельно выполняет код исполнителя.

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