Реализация 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())));
        }
}