Integration tests with WorkManager

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

Content and code samples on this page are subject to the licenses described in the Content License. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.

Last updated 2024年01月03日 UTC.