Сопрограммы: переопределение диспетчера OKHttp для использования ThreadPoolExecutor AsyncTasks, чтобы Espresso мог успешно утверждать

Я переношу приложение, которое использует Retrofit для работы с сопрограммами. В приложении есть некоторые UAT, которые дают сбой, потому что Espresso не ждет завершения сопрограмм и немедленно утверждает.

CoroutineCallAdapterFactory по умолчанию использует Dispatcher OkHttp для выполнения асинхронных запросов, но Espresso отслеживает только пользовательский интерфейс Thread и пул AsyncTaksThread. Одно из решений, о котором я подумал, — заставить Диспетчер OkHttp использовать AsyncTaskThreadPoolExecutor.

val dispatcher = Dispatcher(AsyncTask.THREAD_POOL_EXECUTOR as ExecutorService)
okHttpClientBuilder.dispatcher(dispatcher)

Кажется, это работает, и тесты проходят.

Это плохая идея? Является ли регистрация IdlingResource лучшим вариантом?

4
0
1 148
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Без дополнительных подробностей о вашей настройке я не уверен, поможет ли эта информация, но я упомяну несколько советов, которые могут помочь с вашими тестами:

Конвертировать исполнителей

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

AsyncTask.THREAD_POOL_EXECUTOR.asCoroutineDispatcher()

И, конечно же, это можно использовать в любом месте, где у вас будет что-то вроде Dispatchers.Main, что означает, что вы можете создать область из этого и запустить свои сопрограммы из этой области, и Espresso должен отслеживать базовый пул исполнителей для завершения. Например:

...
val espressoScope = CoroutineScope(AsyncTask.THREAD_POOL_EXECUTOR.asCoroutineDispatcher())
...
espressoScope.launch { api.getBooks() }

Точно так же вы можете делать такие вещи, как:

val asyncTaskContext = AsyncTask.THREAD_POOL_EXECUTOR.asCoroutineDispatcher()
withContext(asyncTaskContext) {
    api.getBooks()
}

// OR:

@Test
fun someAndroidTest() = runBlocking(asyncTaskContext) {
    // espresso logic
}

Присоединяйтесь к вакансиям (рекомендуется)

И последнее, но не менее важное: вы можете присоединяться к любым заданиям, которые вы создаете в своем тесте, и тест будет ждать завершения задания перед выходом. Это похоже на подход, который больше всего поможет в вашей ситуации, поскольку вы действительно просто хотите дождаться завершения сопрограмм:

@Test
fun `first book has a title`() = runBlocking {
    launch {
        // run a function that suspends and takes a while
        val firstBook = api.getAllBooks().first()
        assertNotNull(firstBook.title)
    }.join()
}

Так можно ли использовать пул потоков AsyncTask? Я знаю, что диспетчеры сопрограмм оптимизированы для выполнения поставленной задачи (т. е. Dispatcher.IO оптимизирован для задач ввода-вывода), и я не знаю, оптимизирован ли пул потоков AsyncTask.

Emmanuel 30.05.2019 13:08

Я думал, что AsyncTask API устарел Google?

IgorGanapolsky 31.08.2020 21:04

Другие вопросы по теме