Issue
I am migrating an app that uses Retrofit
to work with coroutines. The app has some UATs that are failing because Espresso does not wait for the coroutines to complete and asserts immediately.
The CoroutineCallAdapterFactory
, by default, uses OkHttp’s Dispatcher
to perform asynchronous requests, but Espresso
only monitors the UI Thread
and AsyncTaks
‘s Thread
pool. One solution I thought of is to force OkHttp
‘s Dispatcher to use AsyncTask
‘s ThreadPoolExecutor
.
val dispatcher Dispatcher(AsyncTask.THREAD_POOL_EXECUTOR as ExecutorService)
okHttpClientBuilder.dispatcher(dispatcher)
This seems to work and the tests pass.
Is this a bad idea? Is registering an IdlingResource
still a better option?
Solution
Without more details of your setup, I’m not sure if this information will help but I’ll mention a few tips that may help with your tests:
Convert Executors
You can also go the other way and create a coroutine dispatcher from any executor using:
AsyncTask.THREAD_POOL_EXECUTOR.asCoroutineDispatcher()
And of course, this can be used anyplace you’d have something like Dispatchers.Main
meaning you can create a scope from this and launch your coroutines from that scope and Espresso should monitor the underlying executor pool for completion. For example:
...
val espressoScope CoroutineScope(AsyncTask.THREAD_POOL_EXECUTOR.asCoroutineDispatcher())
...
espressoScope.launch { api.getBooks() }
Similarly, you can do things like:
val asyncTaskContext AsyncTask.THREAD_POOL_EXECUTOR.asCoroutineDispatcher()
withContext(asyncTaskContext) {
api.getBooks()
}
// OR:
@Test
fun someAndroidTest() runBlocking(asyncTaskContext) {
// espresso logic
}
Join Jobs (recommended)
Last but not least, you can join any jobs that you create in your test and the test will wait until the job completes before exiting. This sounds like the approach that would help most in your situation since you really just want to wait until the coroutines are complete:
@Test
fun `first book has a title`() runBlocking {
launch {
// run a function that suspends and takes a while
val firstBook api.getAllBooks().first()
assertNotNull(firstBook.title)
}.join()
}
Answered By – gMale