简体   繁体   English

如何在使用Retrofit网络请求时使用IdlingResource测试Android UI

[英]How to test Android UI using IdlingResource when using Retrofit network requests

I am writing integration tests that perform actions in the UI which start network calls using Retrofit . 我正在编写集成测试,在UI中执行使用Retrofit启动网络调用的操作。

I know I need to implement a CountingIdlingResource , but I want to do it the correct way (and not reinvent the wheel if it has already been done). 我知道我需要实现一个CountingIdlingResource ,但我想以正确的方式做到这一点(如果已经完成,则不要重新发明轮子)。

Has anyone implemented an IdlingResource in their app's Espresso test suite to wait while network requests execute? 有人在他们的应用程序的Espresso测试套件中实现了一个IdlingResource ,等待网络请求执行吗?

More info here . 更多信息在这里

The most straightforward solution for this: is to basically swap out Retrofit's Thread-pool executor with an AsyncTask one (as recommended by the very helpful Nick from that linked Google group discussion ). 对此最直接的解决方案是:基本上将Retrofit的线程池执行器替换为AsyncTask(根据链接的Google小组讨论中非常有用的Nick推荐)。 I do this like so: 我是这样做的:

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

I'm not sure if this is the most appropriate solution, but it's the quickest most sane one that I could get working. 我不确定这是否是最合适的解决方案,但它是我能够工作的最快最理智的解决方案。 Bare in mind the caveat, that this works only for ICS+. 请记住,这只适用于ICS +。

If you're using RxJava Observables with Retrofit 2.0 then you can use .subscribeOn(Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR)) instead of .subscribeOn(Schedulers.io()) and everything works fine! 如果您正在使用RxJava Observables和Retrofit 2.0,那么您可以使用.subscribeOn(Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR))而不是.subscribeOn(Schedulers.io()) ,一切正常!

OR alternatively you can override RxJavaSchedulersHook, allowing you to just make the change in one location. 或者,您可以覆盖RxJavaSchedulersHook,允许您只在一个位置进行更改。 For example: 例如:

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

note answer below is based on Retrofit 1.6.1 - will update for newest version. 下面的注释答案基于Retrofit 1.6.1 - 将更新为最新版本。 Retrofit 1.9.0 does not allow you to set the HttpExecutor via the RestAdapter.Builder any longer Retrofit 1.9.0不允许您再通过RestAdapter.Builder设置HttpExecutor

The accepted answer is a step in the right direction but it makes me feel uncomfortable. 接受的答案是向正确方向迈出的一步,但这让我感到不舒服。 In practise you would either need to set the AsyncTask.THREAD_POOL_EXECUTOR for live & tests builds OR test builds only. 实际上,您需要为实时和测试构建或测试构建设置AsyncTask.THREAD_POOL_EXECUTOR

Setting for both would mean all your network IO pooling will depend on the aysnc queue implementation, which became serial by default for apps with target versions ICS+ 两者的设置意味着所有网络IO池将依赖于aysnc队列实现, 对于具有目标版本ICS +的应用程序 ,该实现默认为串行

Setting for tests only would mean that your test build is different from your live build, which imho is not a great place to start testing from. 仅为测试设置意味着您的测试版本与您的实时版本不同,而imho不是开始测试的好地方。 Also you may encounter test problems on older devices due to async pool changes. 此外,由于异步池更改,您可能会在旧设备上遇到测试问题。

It is rightly mentioned above that Espresso hooks into AsyncTask.THREAD_POOL_EXECUTOR already. 上面正确地提到Espresso已经挂钩到AsyncTask.THREAD_POOL_EXECUTOR Lets poke around... 让我们四处寻找......

How does it obtain this? 它是如何获得的?

ThreadPoolExecutorExtractor

Who/what uses this? 谁/什么用这个?

BaseLayerModule has provideCompatAsyncTaskMonitor(ThreadPoolExecutorExtractor extractor) which returns an AsyncTaskPoolMonitor BaseLayerModule provideCompatAsyncTaskMonitor(ThreadPoolExecutorExtractor extractor) BaseLayerModule provideCompatAsyncTaskMonitor(ThreadPoolExecutorExtractor extractor) ,它返回一个AsyncTaskPoolMonitor

How does that work? 这是如何运作的? Have a look! 看一看!

AsyncTaskPoolMonitor

Where is it used? 在哪里使用?

UiControllerImpl has method loopMainThreadUntilIdle() which manually calls asyncTaskMonitor.isIdleNow() before checking any user registered idlingResources with idlingResourceRegistry.allResourcesAreIdle() UiControllerImpl有方法loopMainThreadUntilIdle() ,它在使用asyncTaskMonitor.isIdleNow()检查任何用户注册的idlingResources之前手动调用idlingResourceRegistry.allResourcesAreIdle()

Im guessing with Retrofit we can use the RestAdapter.Builder.setExecutors(...) method and pass in our own instance (or version) of the AsyncTaskPoolMonitor using the same http Executor that Retrofit is init on Android with 我猜测Retrofit我们可以使用RestAdapter.Builder.setExecutors(...)方法并使用与Android上的Retrofit init相同的http Executor传入我们自己的AsyncTaskPoolMonitor实例(或版本)

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

(from here ) (从这里

And wrap this in the IdlingResource interface to use in our tests!! 并将其包装在IdlingResource接口中,以便在我们的测试中使用!!

The only question in that as Retrofit makes the callback using a separate Executor on the mainThread that relies on the main Looper, this may result in problems but Im assuming for the moment that Espresso is tied into this as well. 唯一的问题是,由于Retrofit使用mainThread上依赖主Looper的单独Executor进行回调,这可能会导致问题,但我现在假设Espresso也与此相关。 Need to look into this one. 需要研究一下这个。

If you're using Asynctasks, you don't need to do anything because Espresso already knows how to wait for them: it uses AsyncTaskPoolMonitor which is a wrapper around the Asynctask thread pool. 如果你正在使用Asynctasks,你不需要做任何事情,因为Espresso已经知道如何等待它们:它使用AsyncTaskPoolMonitor ,它是Asynctask线程池的包装器。

If you're using you're own thread pool (that was my case), you could use this class that will wrap your executor so that Espresso can know when it's idle. 如果您正在使用自己的线程池(这是我的情况),您可以使用此类来包装您的执行程序,以便Espresso可以知道它何时处于空闲状态。

This great post explains how it works. 这篇精彩的文章解释了它的运作方式 I tried in my project and it's great! 我尝试过我的项目,这很棒! Using dagger, I get a hold of my thread pool and wrapped it in an IdlingResource in a junit @rule. 使用dagger,我得到了一个我的线程池并将其包装在junit @rule中的IdlingResource中。

Retrofit 2 uses okhttp3, which, in turn uses a dispatcher. Retrofit 2使用okhttp3,后者又使用调度程序。 Jake Wharton created this library that monitors the dispatcher for idleness. 杰克沃顿创建了这个监控调度员闲置的库。 You would create the IdlingResource like this: 您可以像这样创建IdlingResource:

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

Be aware that this might not suffice to be used for successful Espresso tests (I've tried) because the IdlingResource might say it's idle just before or after the http call, and your Espresso test would execute and fail instead of waiting. 请注意,这可能不足以用于成功的Espresso测试(我已经尝试过),因为IdlingResource可能会说它在http调用之前或之后处于空闲状态,而Espresso测试将执行并失败而不是等待。

My recommendation for these cases is to use a thread pool to launch any background tasks and make an IdlingResource wrapping this thread pool. 我对这些情况的建议是使用线程池来启动任何后台任务并使IdlingResource包装此线程池。 See this article for more info: https://medium.com/@yair.kukielka/idlingresource-dagger-and-junit-rules-198e3ae791ff 有关详细信息,请参阅此文章: https//medium.com/@yair.kukielka/idlingresource-dagger-and-junit-rules-198e3ae791ff

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用Espresso IdlingResource进行Android测试 - Android test with Espresso IdlingResource 如何在android上使用retrofit2和rxjava2发出多个请求? - How to make multiple requests using retrofit2 and rxjava2 on android? 如何使用Retrofit 2在Android中将应用程序逻辑与网络层分开 - How to separate application logic from network layer in Android using Retrofit 2 如何使用Espresso为RecyclerView中的项目设置IdlingResource? - How to setup IdlingResource for item inside RecyclerView using Espresso? 使用同步调用改造2进行单元测试android - Unit test android using synchronous call retrofit 2 为什么使用 retrofit 在 android 上没有网络连接时收到 503 状态码? - Why I receive 503 status code when there is no network connection on android using retrofit? 带有翻新2的Espresso的IdlingResource - Espresso's IdlingResource with Retrofit 2 在 android 中使用多部分注释时如何发送 object retrofit - how to send an object when using the mutlipart annotation in android retrofit 如何使用 MockWebServer 测试 Retrofit API 调用 - How to test Retrofit API call using MockWebServer 在 android 中使用 retrofit 时如何正确返回 api 响应? - How to correctly return api response when using retrofit in android?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM