Testing
Introduction and Setup
WorkManager provides a work-testing
artifact which helps with unit testing of your workers for Android Instrumentation tests.
To use the work-testing
artifact, you should add it as an androidTestImplementation
dependency in build.gradle
. For more information on this, 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.
@RunWith(AndroidJUnit4.class)
public class BasicInstrumentationTest {
@Before
public void setup() {
Context context = InstrumentationRegistry.getTargetContext();
Configuration config = new Configuration.Builder()
// Set log level to Log.DEBUG to
// make it easier to see why tests failed
.setMinimumLoggingLevel(Log.DEBUG)
// Use a SynchronousExecutor to make it easier to write tests
.setExecutor(new SynchronousExecutor())
.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 its outputData
.
public class EchoWorker extends Worker {
public EchoWorker(Context context, WorkerParameters parameters) {
super(context, parameters);
}
@NonNull
@Override
public Result doWork() {
Data input = getInputData();
if (input.size() == 0) {
return Result.failure();
} else {
return Result.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.
@Test
public void testSimpleEchoWorker() throws Exception {
// Define input data
Data input = new Data.Builder()
.put(KEY_1, 1)
.put(KEY_2, 2)
.build();
// Create request
OneTimeWorkRequest request =
new OneTimeWorkRequest.Builder(EchoWorker.class)
.setInputData(input)
.build();
WorkManager workManager = WorkManager.getInstance();
// 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
WorkInfo workInfo = workManager.getWorkInfoById(request.getId()).get();
Data outputData = 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()
.
@Test
public void testEchoWorkerNoInput() throws Exception {
// Create request
OneTimeWorkRequest request =
new OneTimeWorkRequest.Builder(EchoWorker.class)
.build();
WorkManager workManager = WorkManager.getInstance();
// Enqueue and wait for result. This also runs the Worker synchronously
// because we are using a SynchronousExecutor.
workManager.enqueue(request).getResult().get();
// Get WorkInfo
WorkInfo workInfo = workManager.getWorkInfoById(request.getId()).get();
// Assert
assertThat(workInfo.getState(), is(WorkInfo.State.FAILED));
}
Simulating constraints, delays and periodic work
WorkManagerTestInitHelper
provides you with an instance of TestDriver
which can be used to simulate initialDelay
s, conditions where Constraint
s are met for ListenableWorker
s, and intervals for PeriodicWorkRequest
s.
Testing Initial Delays
Worker
’s 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 WorkRequest
s initial delay as met.
@Test
public void testWithInitialDelay() throws Exception {
// Define input data
Data input = new Data.Builder()
.put(KEY_1, 1)
.put(KEY_2, 2)
.build();
// Create request
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(EchoWorker.class)
.setInputData(input)
.setInitialDelay(10, TimeUnit.SECONDS)
.build();
WorkManager workManager = WorkManager.getInstance();
// Get the TestDriver
TestDriver testDriver = 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
WorkInfo workInfo = workManager.getWorkInfoById(request.getId()).get();
Data outputData = 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.
@Test
public void testWithConstraints() throws Exception {
// Define input data
Data input = new Data.Builder()
.put(KEY_1, 1)
.put(KEY_2, 2)
.build();
// Define constraints
Constraints constraints = new Constraints.Builder()
.setRequiresDeviceIdle(true)
.build();
// Create request
OneTimeWorkRequest request = new OneTimeWorkRequest.Builder(EchoWorker.class)
.setInputData(input)
.setConstraints(constraints)
.build();
WorkManager workManager = WorkManager.getInstance();
TestDriver testDriver = 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
WorkInfo workInfo = workManager.getWorkInfoById(request.getId()).get();
Data outputData = 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.
@Test
public void testPeriodicWork() throws Exception {
// Define input data
Data input = new Data.Builder()
.put(KEY_1, 1)
.put(KEY_2, 2)
.build();
// Create request
PeriodicWorkRequest request =
new PeriodicWorkRequest.Builder(EchoWorker.class, 15, MINUTES)
.setInputData(input)
.build();
WorkManager workManager = WorkManager.getInstance();
TestDriver testDriver = WorkManagerTestInitHelper.getTestDriver();
// Enqueue
workManager.enqueue(request).getResult().get();
// Tells the testing framework the period delay is met
testDriver.setPeriodDelayMet(request.getId());
// Get WorkInfo and outputData
WorkInfo workInfo = workManager.getWorkInfoById(request.getId()).get();
// Assert
assertThat(workInfo.getState(), is(WorkInfo.State.ENQUEUED));
}