The scenario is when the user click on the "Download" button, the data (a music/an image etc.) starts being downloaded from the internet. When the download is finished, the button changes it label text to "Open". The user then click on that "Open" button. What I've done sofar is:
onView(allOf(withId(R.id.button),withText("Download"))).check(matches(isClickable())).perform(click());
try {
Thread.sleep(delayedTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
onView(allOf(withId(R.id.button),withText("Open"))).check(matches(isClickable())).perform(click());
Sometimes the test passes, sometimes it doesnt. The reason sometimes it fails is "No views in hierarchy found matching: (with id: something:id/button and with text: is "Open").
And I know using Thread.sleep in Espresso is bad practice too. I've read about Espresso's Idling Resource, but it doesn't make sense to me and I dont know how can I apply that in this particular case.
Actually idling resources are most difficult points of Espresso.
You should implement it if in your test you have to wait for/sync with some asynchronous background task. By default, Espresso waits for UI operations in the current message queue to process and default AsyncTasks(synchronizes with the default AsyncTask thread pool) to complete before it moves on to the next test operation. Please look into this for AsyncTasks. However if you start an other thread for example communicating with some web service you should use IdlingResource
.
Any Thread.sleep()
that you could need is a signal that you could need an idling resource.
Now about how to write an idling resource. It is easily could be done by developer of the code because they now how they are making this point of evaluation.
It is supposed to be something like this:
public class DownloadIdlingResource extends BaseIdlingResource { ... }
It is supposed to define when this resource is Idle or not.....
The test, that wanted to say Espresso that it needs to wait for this idling resource, supposed to define during test class initialization this idling resource(setUp()
, etc.) or activate it when needed. It means that you have a something to wait but it is not activated yet.
For example, idling resource of download could have a listener to set idling resource to non-idle (setIdle(false)
) when download is started and set back to idle when it is finished (setIdle(true)
). Espresso is continuing all other staff if all idling resources are idle(nothing to wait to be finished).
Implementation details of idling resource strongly depends on the application implementations.
What could be said about idling resource in general:
isIdleNow()
implement the logic when the resource is idle
registerIdleTransitionCallback
will be called by Espresso when the resource is registered and will give you a ResourceCallback
.
When the resource goes from busy to idle, in practice you get the callback from your background task that the operation has finished (map data was downloaded or the download was cancelled) you have to callonTransitionToIdle()
on the registered ResourceCallback
.
In getName()
you have to return the name of the resource which is used for logging.
Please be careful do not have deadlock like idling resources. There is a timeout for idling resources. Test could be interrupted by the message explaining what is bad with the idling resource.
I am sorry do not be able to write more specific examples but it really depends on your application implementation.