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
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.