I am writing integration tests that perform actions in the UI which start network calls using 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).
Has anyone implemented an IdlingResource
in their app's Espresso test suite to wait while network requests execute?
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). 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+.
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!
OR alternatively you can override RxJavaSchedulersHook, allowing you to just make the change in one location. 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.9.0 does not allow you to set the HttpExecutor
via the RestAdapter.Builder
any longer
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.
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+
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. 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. Lets poke around...
How does it obtain this?
ThreadPoolExecutorExtractor
Who/what uses this?
BaseLayerModule
has provideCompatAsyncTaskMonitor(ThreadPoolExecutorExtractor extractor)
which returns an 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()
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
@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!!
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. Need to look into this one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With