Как протестировать пользовательский интерфейс Android с использованием IdlingResource при использовании сетевых настроек Retrofit
Я пишу интеграционные тесты, которые выполняют действия в пользовательском интерфейсе, которые запускают сетевые вызовы с помощью Retrofit.
Я знаю, что мне нужно реализовать CountingIdlingResource
, но я хочу сделать это правильно (а не изобретать колесо, если это уже сделано).
Кто-нибудь реализовал IdlingResource
в своем тестовом наборе приложений Espresso, чтобы ждать, пока выполняются сетевые запросы?
Подробнее здесь.
Ответы
Ответ 1
Самое простое решение для этого: это поменять "Retrofit" исполнитель пула потоков с помощью AsyncTask один (как рекомендовал очень полезный Nick из этого связанный Дискуссия в группе Google). Я делаю это так:
new RestAdapter.Builder()
.setEndpoint(LOCLSET_SERVER_URL)
.setExecutors(AsyncTask.THREAD_POOL_EXECUTOR,
new MainThreadExecutor())
.build();
Я не уверен, что это самое подходящее решение, но это самый быстрый, самый здравый, который я мог бы получить. Не стоит забывать о том, что это работает только для ICS +.
Ответ 2
Примечание, приведенное ниже, основано на Retrofit 1.6.1 - будет обновляться для новейшей версии. Retrofit 1.9.0 не позволяет вам больше HttpExecutor
установить RestAdapter.Builder
Принятый ответ - это шаг в правильном направлении, но это заставляет меня чувствовать себя некомфортно. На практике вам нужно либо установить AsyncTask.THREAD_POOL_EXECUTOR
для live, либо только для тестов.
Настройка для обоих будет означать, что все ваши сетевые IO-объединения будут зависеть от реализации очереди aysnc, которая стала по умолчанию для приложений с целевыми версиями ICS +
Настройка только для тестов означала бы, что ваша тестовая сборка отличается от вашей живой сборки, и имхо не является отличным местом для начала тестирования. Также вы можете столкнуться с проблемами тестирования на старых устройствах из-за изменений в асинхронном пуле.
Понятно, что Espresso
уже втягивается в AsyncTask.THREAD_POOL_EXECUTOR
. Позволяет трястись вокруг...
Как это получить?
ThreadPoolExecutorExtractor
Кто/что использует это?
BaseLayerModule
имеет provideCompatAsyncTaskMonitor(ThreadPoolExecutorExtractor extractor)
, который возвращает AsyncTaskPoolMonitor
Как это работает? Посмотрите!
AsyncTaskPoolMonitor
Где он используется?
UiControllerImpl
имеет метод loopMainThreadUntilIdle()
, который вручную вызывает asyncTaskMonitor.isIdleNow()
перед проверкой зарегистрированных пользователем idlingResources с помощью idlingResourceRegistry.allResourcesAreIdle()
Im guessing with Retrofit мы можем использовать метод RestAdapter.Builder.setExecutors(...)
и передать в нашем собственном экземпляре (или версии) AsyncTaskPoolMonitor
, используя тот же http Executor
, что Retrofit
является init на Android с
@Override Executor defaultHttpExecutor() {
return Executors.newCachedThreadPool(new ThreadFactory() {
@Override public Thread newThread(final Runnable r) {
return new Thread(new Runnable() {
@Override public void run() {
Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
r.run();
}
}, RestAdapter.IDLE_THREAD_NAME);
}
});
}
(из здесь)
И оберните это в интерфейсе IdlingResource
для использования в наших тестах!
Единственный вопрос в том, что в качестве Retrofit
делает обратный вызов с использованием отдельного Executor
на mainThread, который опирается на основной Looper, это может привести к проблемам, но Im принимает на тот момент, что Espresso также привязан к этому, Вам нужно изучить этот.
Ответ 3
Если вы используете RxJava Observables с Retrofit 2.0, вы можете использовать .subscribeOn(Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR))
вместо .subscribeOn(Schedulers.io())
, и все работает отлично!
ИЛИ, альтернативно, вы можете переопределить RxJavaSchedulersHook, позволяя просто внести изменения в одно местоположение. Например:
public MySuperCoolClient() {
if (BuildConfig.DEBUG) {
configureIoSchedulerToUseAsyncTaskThreadPool();
}
this.restApi = new Retrofit.Builder()
.baseUrl(Parameters.endpoint)
.addConverterFactory(GsonConverterFactory.create(gsonBuilder()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
.create(RestApi.class);
}
private void configureIoSchedulerToUseAsyncTaskThreadPool() {
RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() {
@Override
public Scheduler getIOScheduler() {
return Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
}
Ответ 4
Если вы используете Asynctasks, вам не нужно ничего делать, потому что Espresso уже знает, как ждать их: он использует AsyncTaskPoolMonitor, который является оберткой вокруг пула потоков Asynctask.
Если вы используете свой собственный пул потоков (это был мой случай), вы можете использовать этот класс, который обернет вашего исполнителя так что Espresso может знать, когда он простаивает.
Этот отличный пост объясняет, как это работает. Я пробовал в своем проекте, и это здорово! Используя кинжал, я ухватился за пул потоков и завернул его в IdlingResource в junit @rule.
Ответ 5
В Retrofit 2 используется okhttp3, который, в свою очередь, использует диспетчер. Джейк Уортон создал эту библиотеку, которая контролирует диспетчера на праздность. Вы создадите IdlingResource следующим образом:
IdlingResource resource = OkHttp3IdlingResource.create("OkHttp", okHttpClient);
Помните, что этого недостаточно, чтобы использовать успешные тесты Espresso (я пробовал), потому что IdlingResource может сказать, что он простаивает до или после HTTP-вызова, и ваш тест Espresso будет выполняться и сбой вместо ожидания.
Моя рекомендация в этих случаях - использовать пул потоков для запуска любых фоновых задач и сделать IdlingResource, обертывающий этот пул потоков. См. Эту статью для получения дополнительной информации: https://medium.com/@yair.kukielka/idlingresource-dagger-and-junit-rules-198e3ae791ff