nhoxbypass
nhoxbypass

Reputation: 10162

Periodic work not reach running state when unit testing WorkManager

I created a PeriodicWorkRequest from my SyncDatabaseWorker, which like below:

class SyncDatabaseWorker(ctx: Context, params: WorkerParameters) : RxWorker(ctx, params) {

    private val dataManager: DataManager = App.getDataManager()

    override fun createWork(): Single<Result> {
        return Single.create { emitter ->

            dataManager.loadStoresFromServer()
                    .subscribe(object : SingleObserver<List<Store>> {
                        override fun onSubscribe(d: Disposable) {
                        }

                        override fun onSuccess(storeList: List<Store>) {
                            if (!storeList.isEmpty()) {
                                emitter.onSuccess(Result.success())
                            } else {
                                emitter.onSuccess(Result.retry())
                            }
                        }

                        override fun onError(e: Throwable) {
                            emitter.onSuccess(Result.failure())
                        }
                    })
        }
    }

    companion object {
        fun prepareSyncDBWorker(): PeriodicWorkRequest {
            val constraints = Constraints.Builder()
                    .setRequiredNetworkType(NetworkType.CONNECTED)
                    .build()

            val myWorkBuilder = PeriodicWorkRequest.Builder(SyncDatabaseWorker::class.java, 7, TimeUnit.DAYS)
                    .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.DAYS) // Backoff retry after 1 day
                    .setConstraints(constraints)

            return myWorkBuilder.build()
        }
    }
}

Then I write unit test base on Google's guide like this:

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

    @Before
    fun setup() {
        context = InstrumentationRegistry.getInstrumentation().targetContext

        val config = Configuration.Builder()
                .setMinimumLoggingLevel(Log.DEBUG)
                .setExecutor(SynchronousExecutor())
                .build()

        // Initialize WorkManager for instrumentation tests.
        WorkManagerTestInitHelper.initializeTestWorkManager(context, config)
    }

    @Test
    @Throws(Exception::class)
    fun testPeriodicWork_WithConstrains() {
        // Create request
        val request = SyncDatabaseWorker.prepareSyncDBWorker()

        val workManager = WorkManager.getInstance(context)
        val testDriver = WorkManagerTestInitHelper.getTestDriver(context)

        // Enqueue and wait for result.
        workManager.enqueue(request).result.get()

        // Check work request is enqueued
        var workInfo = workManager.getWorkInfoById(request.id).get()
        assertThat(workInfo.state, `is`(WorkInfo.State.ENQUEUED))

        // Tells the testing framework the period delay & all constrains is met
        testDriver!!.setPeriodDelayMet(request.id)
        testDriver.setAllConstraintsMet(request.id)

        // Check work request is running
        workInfo = workManager.getWorkInfoById(request.id).get()
        assertThat(workInfo.state, `is`(WorkInfo.State.RUNNING))
    }
}

It's always in state ENQUEUED, even when period delay & all constrains is met.

Expected: is <RUNNING>
but: was <ENQUEUED>

When I debugged the test & find out that the createWork(): Single<Result> method is also triggered, but why the state is not RUNNING?

May be I'm wrong about the approach, but the documents about unit testing WorkManager is very few now, and I don't know the right way to do it.

Upvotes: 1

Views: 942

Answers (2)

Rahul
Rahul

Reputation: 21134

Given you are executing a PeriodicWorkRequest with a SynchronousExecutor, you will never see the WorkRequest in RUNNING state. It will be done executing before you can assert that it was in RUNNING.

After you return a Result.success() or a Result.failure() in your doWork(), the WorkRequest goes back to ENQUEUED for the next period (given it's a periodic request).

Upvotes: 1

SumirKodes
SumirKodes

Reputation: 563

Since you're using a synchronous executor, you will never actually see your work in the RUNNING state - it should have already executed. I suspect your work is actually being marked for retry and therefore enters the ENQUEUED state again. You should be able to verify this by either setting breakpoints or looking at your logs.

Upvotes: 1

Related Questions