Integration tests with WorkManager
Stay organized with collections
Save and categorize content based on your preferences.
WorkManager provides a
work-testing artifact which helps with testing of your workers.
Setup
To use the work-testing artifact, add it as an androidTestImplementation
dependency in build.gradle.
Groovy
dependencies{ defwork_version="2.5.0" ... // optional - Test helpers androidTestImplementation"androidx.work:work-testing:$work_version" }
Kotlin
dependencies{ valwork_version="2.4.0" ... // optional - Test helpers androidTestImplementation("androidx.work:work-testing:$work_version") }
For more information on adding dependencies, look at the Declaring dependencies section in the WorkManager release notes.
Concepts
work-testing provides a special implementation of WorkManager for test mode,
which is initialized using
WorkManagerTestInitHelper.
The work-testing artifact also provides a
SynchronousExecutor
which makes it easier to write tests in a synchronous manner, without having to
deal with multiple threads, locks, or latches.
Here is an example on how to use all these classes together.
Kotlin
@RunWith(AndroidJUnit4::class) classBasicInstrumentationTest{ @Before funsetup(){ valcontext=InstrumentationRegistry.getTargetContext() valconfig=Configuration.Builder() .setMinimumLoggingLevel(Log.DEBUG) .setExecutor(SynchronousExecutor()) .build() // Initialize WorkManager for instrumentation tests. WorkManagerTestInitHelper.initializeTestWorkManager(context,config) } }
Java
@RunWith(AndroidJUnit4.class) publicclass BasicInstrumentationTest{ @Before publicvoidsetup(){ Contextcontext=InstrumentationRegistry.getTargetContext(); Configurationconfig=newConfiguration.Builder() .setMinimumLoggingLevel(Log.DEBUG) .setExecutor(newSynchronousExecutor()) .build(); // Initialize WorkManager for instrumentation tests. WorkManagerTestInitHelper.initializeTestWorkManager( context,config); } }
Structuring Tests
Now that WorkManager has been initialized in test mode, you are ready to test your workers.
Let’s say you have an EchoWorker which expects some inputData, and simply
copies (echoes) its input to outputData.
Kotlin
classEchoWorker(context:Context,parameters:WorkerParameters) :Worker(context,parameters){ overridefundoWork():Result{ returnwhen(inputData.size()){ 0->Result.failure() else->Result.success(inputData) } } }
Java
publicclass EchoWorkerextendsWorker{ publicEchoWorker(Contextcontext,WorkerParametersparameters){ super(context,parameters); } @NonNull @Override publicResultdoWork(){ Datainput=getInputData(); if(input.size()==0){ returnResult.failure(); }else{ returnResult.success(input); } } }
Basic Tests
Below is an Android Instrumentation test that tests EchoWorker. The main
takeaway here is that testing EchoWorker in test mode is very similar to how
you would use EchoWorker in a real application.
Kotlin
@Test @Throws(Exception::class) funtestSimpleEchoWorker(){ // Define input data valinput=workDataOf(KEY_1to1,KEY_2to2) // Create request valrequest=OneTimeWorkRequestBuilder<EchoWorker>() .setInputData(input) .build() valworkManager=WorkManager.getInstance(applicationContext) // Enqueue and wait for result. This also runs the Worker synchronously // because we are using a SynchronousExecutor. workManager.enqueue(request).result.get() // Get WorkInfo and outputData valworkInfo=workManager.getWorkInfoById(request.id).get() valoutputData=workInfo.outputData // Assert assertThat(workInfo.state,`is`(WorkInfo.State.SUCCEEDED)) assertThat(outputData,`is`(input)) }
Java
@Test publicvoidtestSimpleEchoWorker()throwsException{ // Define input data Datainput=newData.Builder() .put(KEY_1,1) .put(KEY_2,2) .build(); // Create request OneTimeWorkRequestrequest= newOneTimeWorkRequest.Builder(EchoWorker.class) .setInputData(input) .build(); WorkManagerworkManager=WorkManager.getInstance(getApplicationContext()); // Enqueue and wait for result. This also runs the Worker synchronously // because we are using a SynchronousExecutor. workManager.enqueue(request).getResult().get(); // Get WorkInfo and outputData WorkInfoworkInfo=workManager.getWorkInfoById(request.getId()).get(); DataoutputData=workInfo.getOutputData(); // Assert assertThat(workInfo.getState(),is(WorkInfo.State.SUCCEEDED)); assertThat(outputData,is(input)); }
Let’s write another test which makes sure that when EchoWorker gets no input
data, the expected Result is a Result.failure().
Kotlin
@Test @Throws(Exception::class) funtestEchoWorkerNoInput(){ // Create request valrequest=OneTimeWorkRequestBuilder<EchoWorker>() .build() valworkManager=WorkManager.getInstance(applicationContext) // Enqueue and wait for result. This also runs the Worker synchronously // because we are using a SynchronousExecutor. workManager.enqueue(request).result.get() // Get WorkInfo valworkInfo=workManager.getWorkInfoById(request.id).get() // Assert assertThat(workInfo.state,`is`(WorkInfo.State.FAILED)) }
Java
@Test publicvoidtestEchoWorkerNoInput()throwsException{ // Create request OneTimeWorkRequestrequest= newOneTimeWorkRequest.Builder(EchoWorker.class) .build(); WorkManagerworkManager=WorkManager.getInstance(getApplicationContext()); // Enqueue and wait for result. This also runs the Worker synchronously // because we are using a SynchronousExecutor. workManager.enqueue(request).getResult().get(); // Get WorkInfo WorkInfoworkInfo=workManager.getWorkInfoById(request.getId()).get(); // Assert assertThat(workInfo.getState(),is(WorkInfo.State.FAILED)); }
Simulate constraints, delays, and periodic work
WorkManagerTestInitHelper provides you with an instance of
TestDriver which can be used
to simulate initial delay, conditions where constraints are met for
ListenableWorker instances, and, intervals for PeriodicWorkRequest
instances.
Test Initial Delays
Workers can have initial delays. To test EchoWorker with an initialDelay,
rather than having to wait for the initialDelay in your test, you can use
the TestDriver to mark the work request’s initial delay as met using
setInitialDelayMet.
Kotlin
@Test @Throws(Exception::class) funtestWithInitialDelay(){ // Define input data valinput=workDataOf(KEY_1to1,KEY_2to2) // Create request valrequest=OneTimeWorkRequestBuilder<EchoWorker>() .setInputData(input) .setInitialDelay(10,TimeUnit.SECONDS) .build() valworkManager=WorkManager.getInstance(getApplicationContext()) // Get the TestDriver valtestDriver=WorkManagerTestInitHelper.getTestDriver() // Enqueue workManager.enqueue(request).result.get() // Tells the WorkManager test framework that initial delays are now met. testDriver.setInitialDelayMet(request.id) // Get WorkInfo and outputData valworkInfo=workManager.getWorkInfoById(request.id).get() valoutputData=workInfo.outputData // Assert assertThat(workInfo.state,`is`(WorkInfo.State.SUCCEEDED)) assertThat(outputData,`is`(input)) }
Java
@Test publicvoidtestWithInitialDelay()throwsException{ // Define input data Datainput=newData.Builder() .put(KEY_1,1) .put(KEY_2,2) .build(); // Create request OneTimeWorkRequestrequest=newOneTimeWorkRequest.Builder(EchoWorker.class) .setInputData(input) .setInitialDelay(10,TimeUnit.SECONDS) .build(); WorkManagerworkManager=WorkManager.getInstance(myContext); // Get the TestDriver TestDrivertestDriver=WorkManagerTestInitHelper.getTestDriver(); // Enqueue workManager.enqueue(request).getResult().get(); // Tells the WorkManager test framework that initial delays are now met. testDriver.setInitialDelayMet(request.getId()); // Get WorkInfo and outputData WorkInfoworkInfo=workManager.getWorkInfoById(request.getId()).get(); DataoutputData=workInfo.getOutputData(); // Assert assertThat(workInfo.getState(),is(WorkInfo.State.SUCCEEDED)); assertThat(outputData,is(input)); }
Testing Constraints
TestDriver can also be used to mark constraints as met using
setAllConstraintsMet. Here is an example on how you can test a Worker
with constraints.
Kotlin
@Test @Throws(Exception::class) funtestWithConstraints(){ // Define input data valinput=workDataOf(KEY_1to1,KEY_2to2) valconstraints=Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() // Create request valrequest=OneTimeWorkRequestBuilder<EchoWorker>() .setInputData(input) .setConstraints(constraints) .build() valworkManager=WorkManager.getInstance(myContext) valtestDriver=WorkManagerTestInitHelper.getTestDriver() // Enqueue workManager.enqueue(request).result.get() // Tells the testing framework that all constraints are met. testDriver.setAllConstraintsMet(request.id) // Get WorkInfo and outputData valworkInfo=workManager.getWorkInfoById(request.id).get() valoutputData=workInfo.outputData // Assert assertThat(workInfo.state,`is`(WorkInfo.State.SUCCEEDED)) assertThat(outputData,`is`(input)) }
Java
@Test publicvoidtestWithConstraints()throwsException{ // Define input data Datainput=newData.Builder() .put(KEY_1,1) .put(KEY_2,2) .build(); // Define constraints Constraintsconstraints=newConstraints.Builder() .setRequiresDeviceIdle(true) .build(); // Create request OneTimeWorkRequestrequest=newOneTimeWorkRequest.Builder(EchoWorker.class) .setInputData(input) .setConstraints(constraints) .build(); WorkManagerworkManager=WorkManager.getInstance(myContext); TestDrivertestDriver=WorkManagerTestInitHelper.getTestDriver(); // Enqueue workManager.enqueue(request).getResult().get(); // Tells the testing framework that all constraints are met. testDriver.setAllConstraintsMet(request.getId()); // Get WorkInfo and outputData WorkInfoworkInfo=workManager.getWorkInfoById(request.getId()).get(); DataoutputData=workInfo.getOutputData(); // Assert assertThat(workInfo.getState(),is(WorkInfo.State.SUCCEEDED)); assertThat(outputData,is(input)); }
Testing Periodic Work
The TestDriver also exposes a setPeriodDelayMet which can be used to
indicate that an interval is complete. Here is an example of
setPeriodDelayMet being used.
Kotlin
@Test @Throws(Exception::class) funtestPeriodicWork(){ // Define input data valinput=workDataOf(KEY_1to1,KEY_2to2) // Create request valrequest=PeriodicWorkRequestBuilder<EchoWorker>(15,MINUTES) .setInputData(input) .build() valworkManager=WorkManager.getInstance(myContext) valtestDriver=WorkManagerTestInitHelper.getTestDriver() // Enqueue and wait for result. workManager.enqueue(request).result.get() // Tells the testing framework the period delay is met testDriver.setPeriodDelayMet(request.id) // Get WorkInfo and outputData valworkInfo=workManager.getWorkInfoById(request.id).get() // Assert assertThat(workInfo.state,`is`(WorkInfo.State.ENQUEUED)) }
Java
@Test publicvoidtestPeriodicWork()throwsException{ // Define input data Datainput=newData.Builder() .put(KEY_1,1) .put(KEY_2,2) .build(); // Create request PeriodicWorkRequestrequest= newPeriodicWorkRequest.Builder(EchoWorker.class,15,MINUTES) .setInputData(input) .build(); WorkManagerworkManager=WorkManager.getInstance(myContext); TestDrivertestDriver=WorkManagerTestInitHelper.getTestDriver(); // Enqueue and wait for result. workManager.enqueue(request).getResult().get(); // Tells the testing framework the period delay is met testDriver.setPeriodDelayMet(request.getId()); // Get WorkInfo and outputData WorkInfoworkInfo=workManager.getWorkInfoById(request.getId()).get(); // Assert assertThat(workInfo.getState(),is(WorkInfo.State.ENQUEUED)); }