'How to wait till a view has gone in Esspresso tests
I have a TextView that shows a "loading" string... and I need to wait till this view is gone ... I don't have a handle on the Asynctask because this method is running in a IntentService and sends a broadcast when the loading is finished.
Any idea about how to wait in an Espresso test for a change in a view's status? I'll need the same with some strings that will change and need to wait for that... I supposed it's the same case ...
Thanks for the help. There are not many examples or FAQs on the net.
Solution 1:[1]
This has been answered here.
You can handle this case by registering an IdlingResource for your web service with Espresso. Take a look at this write-up.
Most likely, you'll want to use CountingIdlingResource (which uses a simple counter to track when something is idle). This sample test demonstrates how this can be done.
Solution 2:[2]
Preamble
You can define a ViewAction that loops the main thread every 50 milliseconds (or a different time of your choosing) until either the visibility of the View in question changes to View.GONE or a maximum amount of time elapses.
Step 1
Define the ViewAction as follows:
/**
* A [ViewAction] that waits up to [timeout] milliseconds for a [View]'s visibility value to change to [View.GONE].
*/
class WaitUntilGoneAction(private val timeout: Long) : ViewAction {
override fun getConstraints(): Matcher<View> {
return any(View::class.java)
}
override fun getDescription(): String {
return "wait up to $timeout milliseconds for the view to be gone"
}
override fun perform(uiController: UiController, view: View) {
val endTime = System.currentTimeMillis() + timeout
do {
if (view.visibility == View.GONE) return
uiController.loopMainThreadForAtLeast(50)
} while (System.currentTimeMillis() < endTime)
throw PerformException.Builder()
.withActionDescription(description)
.withCause(TimeoutException("Waited $timeout milliseconds"))
.withViewDescription(HumanReadables.describe(view))
.build()
}
}
Step 2
Define a function that creates an instance of this ViewAction when called, as follows:
/**
* @return a [WaitUntilGoneAction] instance created with the given [timeout] parameter.
*/
fun waitUntilGone(timeout: Long): ViewAction {
return WaitUntilGoneAction(timeout)
}
Step 3
Call on this ViewAction in your test method as follows:
onView(withId(R.id.loadingTextView)).perform(waitUntilGone(3000L))
Next steps
You can take this concept and similarly create a WaitForTextAction class that waits until a TextView's text changes to a certain value. In this case, however, you'll probably want to change the Matcher returned by the getConstraints() function from any(View::class.java) to any(TextView::class.java).
Solution 3:[3]
Here's how I handle this case:
public void waitForViewToDisappear(int viewId, long maxWaitingTimeMs) {
long endTime = System.currentTimeMillis() + maxWaitingTimeMs;
while (System.currentTimeMillis() <= endTime) {
try {
onView(allOf(withId(viewId), isDisplayed())).matches(not(doesNotExist()));
} catch (NoMatchingViewException ex) {
return; // view has disappeared
}
}
throw new RuntimeException("timeout exceeded"); // or whatever exception you want
}
Note: matches(not(doesNotExist())) is kind of a "noop" matcher; it is just there to make sure the onView part actually gets run. You could equally well write a ViewAction which does nothing and enclose it in a perform call, but that would be more lines of code, hence why I went with this way.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Community |
| Solution 2 | |
| Solution 3 | Adam Burley |
