Testing with WorkManager 2.1.0 (alpha)

Beginning with version 2.1.0, WorkManager provides new APIs that make it easier to test Worker, ListenableWorker, and the ListenableWorker variants (CoroutineWorker and RxWorker).

Previously, to test your workers you needed to use WorkManagerTestInitHelper to initialize WorkManager. With 2.1.0, using WorkManagerTestInitHelper is optional. You no longer need to use WorkManagerTestInitHelper if all you need to do is test the business logic in the Worker.

Testing ListenableWorker and its variants

To test a ListenableWorker or its variants (CoroutineWorker and RxWorker), you can now use TestListenableWorkerBuilder. This builder helps build instances of ListenableWorker that can be used for the purpose of testing the Worker's business logic.

For example, suppose we need to test a CoroutineWorker which looks like this:

Kotlin

class SleepWorker(context: Context, parameters: WorkerParameters) :
    CoroutineWorker(context, parameters) {
    override suspend fun doWork(): Result {
        delay(1000) // milliseconds
        return Result.success()
    }
}

Java

public class SleepWorker extends ListenableWorker {
    private final ResolvableFuture<Result> mResult;
    private final Handler mHandler;
    private final Object mLock;
    private Runnable mRunnable;
    public SleepWorker(
            @NonNull Context context,
            @NonNull WorkerParameters workerParameters) {
        super(context, workerParameters);
        mHandler = new Handler(Looper.getMainLooper());
        mResult = new ResolvableFuture<>();
        mLock = new Object();
    }

    @NonNull
    @Override
    public ListenableFuture<Result> startWork() {
        mRunnable = new Runnable() {
            @Override
            public void run() {
                synchronized (mLock) {
                    mResult.set(Result.success());
                }
            }
        };

        mHandler.postDelayed(mRunnable, 1000L);
        return mResult;
    }

    @Override
    public void onStopped() {
        super.onStopped();
        if (mRunnable != null) {
            mHandler.removeCallbacks(mRunnable);
        }
        synchronized (mLock) {
            if (!mResult.isDone()) {
                mResult.set(Result.failure());
            }
        }
    }
}

To test SleepWorker, we first create an instance of the Worker using TestListenableWorkerBuilder. This builder can also be used to set tags, inputData, runAttemptCount, and so on. For details, see the TestListenableWorker reference page.

Kotlin

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

    @Before
    fun setUp() {
        context = ApplicationProvider.getApplicationContext()
    }

    @Test
    fun testSleepWorker() {
        // Kotlin code can use the TestListenableWorkerBuilder extension to
        // build the ListenableWorker
        val worker = TestListenableWorkerBuilder<SleepWorker>(context).build()
        runBlocking {
            val result = worker.doWork()
            assertThat(result, `is`(Result.success()))
        }
    }
}

Java

@RunWith(AndroidJUnit4.class)
public class SleepWorkerJavaTest {
    private Context mContext;

    @Before
    public void setUp() {
        mContext = ApplicationProvider.getApplicationContext();
    }

    @Test
    public void testSleepWorker() throws Exception {
       ListenableWorker worker =
           TestListenableWorkerBuilder.from(mContext, SleepWorker.class)
                   .build();

        Result result = worker.startWork().get();
        assertThat(result, is(Result.success()));
    }
}

Testing Workers

Let’s say we have a Worker which looks like this:

Kotlin

class SleepWorker(context: Context, parameters: WorkerParameters) :
    Worker(context, parameters) {

    companion object {
        const val SLEEP_DURATION = "SLEEP_DURATION"
    }

    override fun doWork(): Result {
        // Sleep on a background thread.
        val sleepDuration = inputData.getLong(SLEEP_DURATION, 1000)
        Thread.sleep(sleepDuration)
        return Result.success()
    }
}

Java

public class SleepWorker extends Worker {
    public static final String SLEEP_DURATION = "SLEEP_DURATION";

    public SleepWorker(
            @NonNull Context context,
            @NonNull WorkerParameters workerParameters) {
        super(context, workerParameters);
    }

    @NonNull
    @Override
    public Result doWork() {
        try {
            long duration = getInputData().getLong(SLEEP_DURATION, 1000);
            Thread.sleep(duration);
        } catch (InterruptedException ignore) {
        }
        return Result.success();
    }
}

To test this Worker, you can now use TestWorkerBuilder. The main difference between TestWorkerBuilder and a TestListenableWorkerBuilder is, TestWorkerBuilder lets you specify the background Executor used to run the Worker.

Kotlin

// Kotlin code can use the TestWorkerBuilder extension to
// build the Worker
@RunWith(AndroidJUnit4::class)
class SleepWorkerTest {
    private lateinit var context: Context
    private lateinit var executor: Executor

    @Before
    fun setUp() {
        context = ApplicationProvider.getApplicationContext()
        executor = Executors.newSingleThreadExecutor()
    }

    @Test
    fun testSleepWorker() {
        val worker = TestWorkerBuilder<SleepWorker>(
            context = context,
            executor = executor,
            inputData = workDataOf("SLEEP_DURATION" to 10000L)
        ).build()

        val result = worker.doWork()
        assertThat(result, `is`(Result.success()))
    }
}

Java


@RunWith(AndroidJUnit4.class)
public class SleepWorkerJavaTest {
    private Context mContext;
    private Executor mExecutor;

    @Before
    public void setUp() {
        mContext = ApplicationProvider.getApplicationContext();
        mExecutor = Executors.newSingleThreadExecutor();
    }

    @Test
    public void testSleepWorker() {
        Data inputData = new Data.Builder()
                .putLong("SLEEP_DURATION", 10_000L)
                .build();

        SleepWorker worker =
                (SleepWorker) TestWorkerBuilder.from(mContext,
                        SleepWorker.class,
                        mExecutor)
                        .setInputData(inputData)
                        .build();

        Result result = worker.doWork();
        assertThat(result, is(Result.success()));
    }
}