Как протестировать пользовательский интерфейс 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