Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Espresso and postDelayed

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)

like image 901
Matt Avatar asked Aug 21 '15 14:08

Matt


1 Answers

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:

  1. 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.

  2. Sleep in your test code, but you don't like that approach already.

  3. 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

like image 186
Gil Vegliach Avatar answered Nov 03 '22 22:11

Gil Vegliach