If the unreliability experienced with Wait for it...a deep dive into Espresso's Idling Resources was caused by using WebChromeClient
, consider using WebViewClient
instead.
I found that WebViewClient
fires onPageFinished()
reliably when a web page finishes loading. WebChromeClient
and WebViewClient
are similar but WebViewClient
is more general in that WebChromeClient
handles the Chrome-specific aspects of the WebView (see What's the difference between setWebViewClient vs. setWebChromeClient?).
In a sample project, https://github.com/mosofsky/how-to-coredux, I register the onPageFinished()
function to the WebView
as follows:
playVideoWebView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
return false
}
override fun onPageFinished(view: WebView?, url: String?) {
if (null != url && url != ABOUT_URL) {
// the next line dispatches an action that calls decrement()
howToViewModel.dispatchAction(LoadVideo_Finish)
}
}
}
I hope this is helpful although admittedly I have not addressed the other drawbacks you mentioned. As for the lack of getWebChromeClient()
, that seems to be available now. As for requiring a specific WebView instance, I don't know how to avoid that. Somehow you have to make the WebView decrement the idling resource counter when it's finished loading. It seems like any solution would have to touch a specific instance of the WebView in some way to complete this registration process. Some developers might feel it's tedious or inappropriate to litter their production code with CountingIdlingResource
's calls to increment()
and decrement()
.
To minimize the amount of code instrumented with idling resource counting calls, you can isolate these calls to what I'll call a "side effect" of asynchronous code. For this, I've found using a Redux data store can be very helpful, for example CoRedux. When an asynchronous action begins, you can update the state to "in progress"; when it ends, clear that "in progress" flag. The "side effect" can be any mechanism that listens for "in progress". CoRedux literally has a construct called a "side effect" that you can use, but any mechanism that responds to state changes is fine.
In the side effect for the how-to-coredux sample project, it calls increment()
and decrement()
like this:
when (action) {
is Initialize_Start, is ShowVideoFragment_Start, LoadVideo_Start, HideVideoFragment_Start -> {
espressoTestIdlingResource.increment()
}
Initialize_Finish, ShowVideoFragment_Finish, LoadVideo_Finish, HideVideoFragment_Finish -> {
espressoTestIdlingResource.decrement()
}
}
By isolating the idling resource logic to a side effect, an entire Android project could rely on a single piece of code for calling increment()
/decrement()
every time any asynchronous UI event begins/ends, respectively.