mars8
mars8

Reputation: 1236

How to run CoroutineWorker synchronously with Android WorkManager?

I use WorkManager in my app to chain multiple CoroutineWorkers together. I am trying to test my app and have these workers run synchronously before continuing with rest of test.

As CoroutineWorker is a suspend function it does not use the SynchronousExecutor() provided to the workmanager Configuration. The Android Docs mentions that CoroutineContext can be provided to customise the thread the CoroutineWorker is run on.

How can I provide a CoroutineContext and have the worker run synchronously with the test code?

@Test
fun LaunchLongRunningCoroutineworkersWithWorkManager_FailsDueToAsynchronosity() {
    hiltRule.inject()
    context = InstrumentationRegistry.getInstrumentation().targetContext
    val config = Configuration.Builder()
        .setWorkerFactory(delegatingWorkerFactory)
        .setExecutor(SynchronousExecutor())
        .build()
    WorkManagerTestInitHelper.initializeTestWorkManager(context, config)

    val workRequest1 = OneTimeWorkRequestBuilder<LongRunningCoroutineWorker1>()
        .build()

    val workRequest2 = OneTimeWorkRequestBuilder<LongRunningCoroutineWorker2>()
        .build()

    val workManager = WorkManager.getInstance(context)
    workManager.beginUniqueWork("WORKNAME",ExistingWorkPolicy.REPLACE, workRequest1)
        .then(workRequest2)
        .enqueue()

    val workInfo1 = workManager.getWorkInfoById(workRequest1.id).get()
    val workInfo2 = workManager.getWorkInfoById(workRequest1.id).get()

    /*fails expected: State.SUCCEEDED was: State.RUNNING*/
    assertThat(workInfo1.state).isEqualTo(WorkInfo.State.SUCCEEDED) 
    assertThat(workInfo2.state).isEqualTo(WorkInfo.State.SUCCEEDED)
}
class LongRunningCoroutineWorker1(
    val context: Context,
    val parameters: WorkerParameters,
) : CoroutineWorker(context, parameters) {
    override suspend fun doWork(): Result {
        return withContext(Dispatchers.IO) {
            /*simulate long work*/
            delay(5000)
            Result.success()
        }
    }
}

Upvotes: 0

Views: 2502

Answers (1)

pfmaggi
pfmaggi

Reputation: 6476

Do not use the TestInitHelper with CoroutineWorkers (or RxWorker). That helper function has been built for Worker classes.

WorkManager v2.1 introduces a new set of APIs to support a simpler way to test ListenableWorker classes and, as a consequence, CoroutineWorker. In our code we're going to use one of these new API: TestListenableWorkerBuilder.

Additional information is available in WorkManager's testing guide and a sample is available in the coroutine codelab, that also covers WorkManager.

In your case you should have something like:

@Test
fun LaunchLongRunningCoroutineworkersWithWorkManager_FailsDueToAsynchronosity() {
    hiltRule.inject()
    context = InstrumentationRegistry.getInstrumentation().targetContext

    val worker1 = TestListenableWorkerBuilder<LongRunningCoroutineWorker1>(context)
                  .setWorkerFactory(delegatingWorkerFactory)
                  .build()

    // "Manually" start the work
    val result1 = worker1.startWork().get()
    assertThat(result).isEqualTo(Result.success())

    val worker2 = TestListenableWorkerBuilder<LongRunningCoroutineWorker2>(context)
                  .setWorkerFactory(delegatingWorkerFactory)
                  .build()

    // "Manually" start the work
    val result2 = worker1.startWork().get()
    assertThat(result).isEqualTo(Result.success())
}

Upvotes: 1

Related Questions