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

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

CoroutineCallAdapterFactory по умолчанию использует OkHttp Dispatcher для выполнения асинхронных запросов, но Espresso контролирует только пул пользовательского интерфейса Thread и AsyncTaks Thread. Одно из решений, о котором я подумал, - заставить Диспетчера OkHttp использовать AsyncTask ThreadPoolExecutor.

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

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

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

1 Ответ

0 голосов
/ 30 мая 2019

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

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

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

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