Реализация Testing Worker

WorkManager предоставляет API для тестирования Worker , ListenableWorker и вариантов ListenableWorker ( CoroutineWorker и RxWorker ).

Тестирующие работники

Допустим, у нас есть Worker , который выглядит так:


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

   
override fun doWork(): Result {
       
// Sleep on a background thread.
       
Thread.sleep(1000)
       
return Result.success()
   
}
}


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

   
@NonNull
   
@Override
   
public Result doWork() {
       
try {
           
Thread.sleep(1000);
       
} catch (InterruptedException ignore) {
return Result.success();
       
}
   
}
}

Чтобы протестировать этого Worker , вы можете использовать TestWorkerBuilder . Этот конструктор помогает создавать экземпляры Worker , которые можно использовать для тестирования бизнес-логики.


// Kotlin code uses 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
       
).build()

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


@RunWith(AndroidJUnit4.class)
public class SleepWorkerJavaTest {
   
private Context context;
   
private Executor executor;

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

   
@Test
   
public void testSleepWorker() {
       
SleepWorker worker =
               
(SleepWorker) TestWorkerBuilder.from(context,
                       
SleepWorker.class,
                        executor
)
                       
.build();

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

TestWorkerBuilder также можно использовать для установки тегов, таких как inputData или runAttemptCount , чтобы можно было проверять состояние рабочего процесса изолированно. Рассмотрим пример, в котором SleepWorker принимает продолжительность сна в качестве входных данных, а не как константу, определенную в рабочем процессе:


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

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

   
companion object {
        const
val SLEEP_DURATION = "SLEEP_DURATION"
   
}
}


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

В SleepWorkerTest вы можете предоставить эти входные данные вашему TestWorkerBuilder , чтобы удовлетворить потребности SleepWorker .


// Kotlin code uses 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 1000L)
       
).build()

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


@RunWith(AndroidJUnit4.class)
public class SleepWorkerJavaTest {
   
private Context context;
   
private Executor executor;

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

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

       
SleepWorker worker =
               
(SleepWorker) TestWorkerBuilder.from(context,
                       
SleepWorker.class, executor)
                       
.setInputData(inputData)
                       
.build();

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

Дополнительные сведения об API TestWorkerBuilder см. на справочной странице TestListenableWorkerBuilder , суперкласса TestWorkerBuilder .

Тестирование ListenableWorker и его вариантов

Чтобы протестировать ListenableWorker или его варианты ( CoroutineWorker и RxWorker ), используйте TestListenableWorkerBuilder . Основное различие между TestWorkerBuilder и TestListenableWorkerBuilder заключается в том, что TestWorkerBuilder позволяет указать фоновый Executor , используемый для запуска Worker , тогда как TestListenableWorkerBuilder полагается на логику потоков реализации ListenableWorker .

Например, предположим, что нам нужно протестировать CoroutineWorker , который выглядит следующим образом:

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

Чтобы протестировать SleepWorker , мы сначала создаем экземпляр Worker с помощью TestListenableWorkerBuilder , а затем вызываем его функцию doWork внутри сопрограммы.

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

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

   
@Test
   
fun testSleepWorker() {
       
val worker = TestListenableWorkerBuilder<SleepWorker>(context).build()
        runBlocking
{
           
val result = worker.doWork()
            assertThat
(result, `is`(Result.success()))
       
}
   
}
}

runBlocking имеет смысл использовать в качестве построителя сопрограмм для ваших тестов, так что любой код, который будет выполняться асинхронно, вместо этого будет выполняться параллельно.

Тестирование реализации RxWorker аналогично тестированию CoroutineWorker , поскольку TestListenableWorkerBuilder может обрабатывать любой подкласс ListenableWorker . Рассмотрим версию SleepWorker , которая использует RxJava вместо сопрограмм.


class SleepWorker(
    context
: Context,
    parameters
: WorkerParameters
) : RxWorker(context, parameters) {
   
override fun createWork(): Single<Result> {
       
return Single.just(Result.success())
           
.delay(1000L, TimeUnit.MILLISECONDS)
   
}
}


public class SleepWorker extends RxWorker {
   
public SleepWorker(@NonNull Context appContext,
@NonNull WorkerParameters workerParams) {
       
super(appContext, workerParams);
   
}

   
@NonNull
   
@Override
   
public Single<Result> createWork() {
       
return Single.just(Result.success())
               
.delay(1000L, TimeUnit.MILLISECONDS);
   
}
}

Версия SleepWorkerTest , тестирующая RxWorker может выглядеть аналогично версии, тестирующей CoroutineWorker . Вы используете тот же TestListenableWorkerBuilder , но теперь вызываете функцию createWork RxWorker . createWork возвращает Single , который вы можете использовать для проверки поведения вашего работника. TestListenableWorkerBuilder справляется с любыми сложностями потоковой обработки и параллельно выполняет ваш рабочий код.


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

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

   
@Test
   
fun testSleepWorker() {
       
val worker = TestListenableWorkerBuilder<SleepWorker>(context).build()
        worker
.createWork().subscribe { result ->
            assertThat
(result, `is`(Result.success()))
       
}
   
}
}


@RunWith(AndroidJUnit4.class)
public class SleepWorkerTest {
   
private Context context;

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

   
@Test
   
public void testSleepWorker() {
       
SleepWorker worker = TestListenableWorkerBuilder.from(context, SleepWorker.class)
               
.build();
        worker
.createWork().subscribe(result ->
                assertThat
(result, is(Result.success())));
       
}
}