I have an activity which is using a postDelayed call:
public class SplashActivity extends Activity {
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(...);
handler.postDelayed(new Runnable() {
public void run() { finish(); }
}, 3000L);
}
}
This runs at app startup, and i need to navigate it and my login screen. However, the UIController's loopMainThreadUntilIdle doesn't seem to take the underlying MessageQueue in the handler into account. As such, this action finishes immediately while there is still messages in the queue.
onView(withId(R.id.splash_screen)).perform(new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isAssignableFrom(View.class);
}
@Override
public String getDescription() {
return "";
}
@Override
public void perform(final UiController uiController, final View view) {
uiController.loopMainThreadUntilIdle();
}
});
I've been unable to figure out how to block until the queue is drained. Android itself is preventing me from doing a lot of things i would have tried (like extending Handler and overriding the postDelayed method, etc...)
Anyone have any suggestions on how to handle postDelayed?
I'd rather avoid uiController.loopMainThreadForAtLeast, which seems hacky (like a Thread.sleep would)
When Espresso waits, it actually does take in account MessageQueue
, but in a different way from what you think. To be idle, the queue must either be empty, or have tasks to be run in more than 15 milliseconds from now.
You can check the code yourself, especially the method loopUntil()
in UiControllerImpl.java
and the file QueueInterrogator.java
. In the latter file you will also find the logic of how Espresso checks the MessageQueue
(method determineQueueState()
).
Now, how to solve your problem? There are many ways:
Use AsyncTask
instead of Handler
, sleeping on the background thread and executing actions onPostExecute()
. This does the trick because Espresso will wait for AsyncTask
to finish, but you might not like the overhead of another thread.
Sleep in your test code, but you don't like that approach already.
Write your custom IdlingResource
: this is a general mechanism to let Espresso know when something is idle so that it can run actions and assertions. For this approach you could:
Use the class CountingIdlingResource
that comes with Espresso
Call increment()
when you post your runnable and decrement()
inside the runnable after your logic has run
Register your IdlingResource
in the test setup and unregister it in the tear down
See also: docs and sample, another sample
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With