Wait until view become visible with IdleResource

F1sher picture F1sher · Oct 7, 2015 · Viewed 13.4k times · Source

I am writing Instrumentation tests with usage of Espresso 2.2.

Flow I want to test:

  1. radioButton clicked by test
  2. onClick launches request to API
  3. after every time different time I receive response
  4. positive response triggers interface method that is called in activity
  5. onRequestSuccess I am making additional panel on screen named vSetupAmount visible

I want to register IdleResource after click on radioButton so it waits until vSetupAmount becomes visible. But I can't make it work. Please tell me what am I doing wrong.

I have written such IdleResource:

public class AmountViewIdlingResource implements IdlingResource {

    private CountriesAndRatesActivity activity;
    private ResourceCallback callback;

    private SetupAmountView amountView;

    public AmountViewIdlingResource(CountriesAndRatesActivity activity) {
        this.activity = activity;

        amountView = (SetupAmountView) this.activity.findViewById(R.id.vSetupAmount);
    }

    @Override public String getName() {
        return "Amount View idling resource";
    }

    @Override public boolean isIdleNow() {
        callback.onTransitionToIdle();
        return amountView.getVisibility() == View.VISIBLE;
    }

    @Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
        this.callback = resourceCallback;
    }

}

So I am passing activity to IdleResource, link view with variable. I understand that IdleResource won't let test go through until isIdleNow() returns value true. So if view is View.GONE then it won't go further.

How it looks in test:

    // click of radioButton picked from radioGroup
    onView(withId(rgDeliveries.getChildAt(id).getId())).perform(scrollTo(), click());

    // wait for view to become visible
    Espresso.registerIdlingResources(new AmountViewIdlingResource(getActivity()));

    // go to button on view
    onView(withId(R.id.btnGetStarted)).perform(scrollTo());

    // unregister idle resource
    for (  IdlingResource resource : getIdlingResources()) {
        Espresso.unregisterIdlingResources(resource);
    }

So I get my click on radioButton. IdleResource is successfully registered but nothing happens. On my device API response comes. vSetupAmount is being displayed but

amountView.getVisibility() == View.VISIBLE;

which is being checked forever (but I see my view on screen) always returns false.

What am I doing wrong?

Answer

Cosmin Radu picture Cosmin Radu · Jan 29, 2016

I know this might be too late, but my answer might help someone.

  1. When you instantiate and register your AmountViewIdlingResource, keep a reference to it and only unregister that reference, don't unregister all IdlingResources.
  2. Your implementation of the isIdleNow() method should only call the callback's onTransitionToIdle() method if your idling resource is really idle not every time.

I needed to do something similar, to wait for a button to appear on the screen, which in turn only got to be visible after an HTTP call finished. My implementation of a view visibility Idling Resource is as follows:

public class ViewVisibilityIdlingResource implements IdlingResource {

    private final View mView;
    private final int mExpectedVisibility;

    private boolean mIdle;
    private ResourceCallback mResourceCallback;

    public ViewVisibilityIdlingResource(final View view, final int expectedVisibility) {
        this.mView = view;
        this.mExpectedVisibility = expectedVisibility;
        this.mIdle = false;
        this.mResourceCallback = null;
    }

    @Override
    public final String getName() {
        return ViewVisibilityIdlingResource.class.getSimpleName();
    }

    @Override
    public final boolean isIdleNow() {
        mIdle = mIdle || mView.getVisibility() == mExpectedVisibility;

        if (mIdle) {
            if (mResourceCallback != null) {
                mResourceCallback.onTransitionToIdle();
            }
        }

        return mIdle;
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
        mResourceCallback = resourceCallback;
    }

}

Hope this helps