【问题标题】:How to test Android UI using IdlingResource when using Retrofit network requests使用 Retrofit 网络请求时如何使用 IdlingResource 测试 Android UI
【发布时间】:2014-06-02 06:33:57
【问题描述】:

我正在编写集成测试,在 UI 中执行操作,使用 Retrofit 启动网络调用。

我知道我需要实现一个CountingIdlingResource,但我想以正确的方式来做(如果已经完成,就不要重新发明轮子)。

是否有人在其应用的 Espresso 测试套件中实现了 IdlingResource 以等待网络请求执行?

更多信息here

【问题讨论】:

  • 我从未使用过 Retrofit,但是通过浏览他们的 API,当您在 AsyncTask 中执行 Client.execute 时,您不需要 IdlingResource,因为 AsyncTask 的线程池由 Espresso 监控。如果由于某种原因您不能使用 AsyncTask,您仍然可以通过 AsyncTask.THREAD_POOL_EXECUTOR.submit() 启动任务来从该线程池监控中受益。使用 IdlingResources 有很多(可解决的)陷阱,根据我的经验,最好尽可能少。
  • Retrofit 使用它自己的 Threadpool/Executor,它不使用 AsyncTasks。 IdlingResource 必须在其中实现,我只是想知道其他人是否已经尝试/完成了这个。
  • 我已经将自定义 IdlingResources 和 CountingIdlingResource 用于各种任务,包括网络请求。如果不了解更多关于 UI 操作如何启动请求以及如何处理响应/取消的信息,就很难给出具体的建议。要么使用链接示例中的修饰方法,要么实现一个简单的侦听器 API,测试类可以将 IdlingResource 连接到该 API,以了解何时等待网络请求完成以及何时完成或中止。
  • 我正在寻找专门使用Retrofit 及其RestAdapter.Builder().setExecutor(...) 方法实现此功能的人。如果我没有得到答案也没关系,但我会在编写自己的实现之前稍等片刻。
  • @AustynMahoney 我正在寻找完全相同的东西(推荐的方法),您的搜索有什么更新吗?

标签: android integration-testing retrofit android-espresso


【解决方案1】:

对此最直接的解决方案:基本上是用 AsyncTask 替换 Retrofit 的线程池执行器(正如非常有帮助的 Nick 从 linked Google group discussion 推荐的那样)。我是这样做的:

new RestAdapter.Builder()
               .setEndpoint(LOCLSET_SERVER_URL)
               .setExecutors(AsyncTask.THREAD_POOL_EXECUTOR,
                             new MainThreadExecutor())
               .build();

我不确定这是否是最合适的解决方案,但它是我能开始工作的最快最理智的解决方案。请记住,这仅适用于 ICS+。

【讨论】:

  • 不知道我是如何错过了 Nick Moukhine 在那个线程中的答案。这正是我在使用 Espresso、Retrofit 和 Dagger 时所需要的。
  • +1 这是一个很好的建议,我今天一直在四处寻找这些东西。奇怪的是,我的 Retrofit + Espresso 涉及异步改造调用的测试只是开箱即用,所以我不知道 espresso 现在是否涵盖了默认改造的工作方式?
  • 应该只添加到通过 DI 进行的测试中还是可以在生产中使用?
  • 如果这样做,请确保禁用所有通知 ProgressBar。使用indeterminateOnly=falsesetVisibility(View.GONE) 否则会挂起然后超时。
【解决方案2】:

如果您在 Retrofit 2.0 中使用 RxJava Observables,那么您可以使用 .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);
         }
      });
   }

【讨论】:

    【解决方案3】:

    下面的注释答案基于 Retrofit 1.6.1 - 将更新为最新版本。 Retrofit 1.9.0 不再允许您通过RestAdapter.Builder 设置HttpExecutor

    接受的答案是朝着正确方向迈出的一步,但这让我感到不舒服。在实践中,您需要为实时和测试构建或仅测试构建设置AsyncTask.THREAD_POOL_EXECUTOR

    设置这两者意味着你所有的网络 IO 池将取决于 aysnc 队列实现,它变成了serial by default for apps with target versions ICS+

    仅设置测试意味着您的测试版本与您的实时版本不同,恕我直言,这不是开始测试的好地方。此外,由于异步池更改,您可能在旧设备上遇到测试问题。

    上面正确地提到Espresso 已经与AsyncTask.THREAD_POOL_EXECUTOR 挂钩。让我们四处寻找......

    它是如何获得这个的?

    ThreadPoolExecutorExtractor

    谁/什么使用这个?

    BaseLayerModuleprovideCompatAsyncTaskMonitor(ThreadPoolExecutorExtractor extractor),它返回一个 AsyncTaskPoolMonitor

    它是如何工作的?看看吧!

    AsyncTaskPoolMonitor

    用在什么地方?

    UiControllerImpl 有方法 loopMainThreadUntilIdle() 在检查任何用户使用idlingResourceRegistry.allResourcesAreIdle() 注册的idlingResources 之前手动调用asyncTaskMonitor.isIdleNow()

    我猜想通过 Retrofit 我们可以使用 RestAdapter.Builder.setExecutors(...) 方法并使用相同的 http Executor 传递我们自己的 AsyncTaskPoolMonitor 实例(或版本),Retrofit 在 Android 上是 init

    @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);
            }
          });
        }
    

    (来自here

    并将其包装在IdlingResource 接口中以在我们的测试中使用!!

    唯一的问题是 Retrofit 在依赖于主 Looper 的 mainThread 上使用单独的 Executor 进行回调,这可能会导致问题,但我暂时假设浓缩咖啡也与此有关。需要研究一下这个。

    【讨论】:

      【解决方案4】:

      Retrofit 2 使用 okhttp3,而后者又使用调度程序。 Jake Wharton 创建了this 库来监视调度程序的空闲状态。您可以像这样创建 IdlingResource:

      IdlingResource resource = OkHttp3IdlingResource.create("OkHttp", okHttpClient);
      

      请注意,这可能不足以用于成功的 Espresso 测试(我已经尝试过),因为 IdlingResource 可能会在 http 调用之前或之后说它处于空闲状态,并且您的 Espresso 测试将执行并失败而不是等待。

      对于这些情况,我的建议是使用线程池来启动任何后台任务并制作一个 IdlingResource 包装此线程池。有关详细信息,请参阅本文:https://medium.com/@yair.kukielka/idlingresource-dagger-and-junit-rules-198e3ae791ff

      【讨论】:

        【解决方案5】:

        如果您使用 Asynctasks,则无需执行任何操作,因为 Espresso 已经知道如何等待它们:它使用 AsyncTaskPoolMonitor,它是 Asynctask 线程池的包装器。

        如果您使用的是自己的线程池(这是我的情况),您可以使用 this 类来包装您的执行程序,以便 Espresso 可以知道它何时空闲。

        This 很棒的帖子解释了它是如何工作的。我在我的项目中尝试过,它很棒!使用 dagger,我获得了我的线程池并将其包装在 junit @rule 中的 IdlingResource 中。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-08-14
          • 2018-05-30
          • 2014-08-25
          • 2015-12-11
          • 2021-07-26
          • 2019-12-08
          • 2014-05-20
          • 1970-01-01
          相关资源
          最近更新 更多