Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Espresso idling resource with multiple activities

I have a firstActivity that launches the secondActivity, where in the secondActivity I have a loading Dialog (not AsyncTask), and I need to make Espresso wait until the dialog disappears before it continues with the test.
Where do I have to implement the IdlingResource? How can I make it wait for the dismissDialog() function?

Here is what I've tried to do:

  class DocumentLoadingIdlingResource implements IdlingResource {
    private ResourceCallback callback;

    @Override
    public String getName() {
      return "Documnet loading idling resource";
    }

    @Override
    public boolean isIdleNow() {
      Activity activity;
      try {
        activity = getCurrentActivity();
      } catch (Throwable e) {
        return false;
      }

      if(activity.getClass().getName().equals(EditorActivity.class.getName())
            && activity.loadingDialogShowing() == false) {
        return false;
      }

      return true;
    }

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

  Activity getCurrentActivity() throws Throwable {
    getInstrumentation().waitForIdleSync();
    final Activity[] activity = new Activity[1];
    runTestOnUiThread(new Runnable() {
      @Override
      public void run() {
        java.util.Collection<Activity> activites = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED);
        activity[0] = com.google.common.collect.Iterables.getOnlyElement(activites);
    }});
    return activity[0];
  }

This class is implemented in the test class.

like image 847
Omar Avatar asked Aug 24 '14 09:08

Omar


2 Answers

There are a few problems here:

  1. Your isIdleNow() calls getCurrentActivity() which calls waitForIdleSync() and runTestOnUiThread(). isIdleNow Javadoc says: "Espresso will always call this method from the main thread, therefore it should be non-blocking and return immediately." So this won't work as is, but you could call getActivitiesInStage directly from isIdleNow.
  2. Your other issue is that you store the reference to ResourceCallback but never invoke onTransitionToIdle, also you should allow for the possibility of more than one ResourceCallback being registered and call onTransitionToIdle on all of the callbacks.

You can do the following:

  1. Copy/Paste IdlingResource into your app as com.mycompany.IdlingResource.
  2. Then have your Activity implement that interface and make sure to call onTransitionToIdle when the dialog goes away and make sure isIdleNow returns false iff the dialog is showing.
  3. In your test code, write a "IdlingResourceAdapter" that wraps com.mycompany.IdlingResource and turns it into an Espresso IdlingResource and register that with Espresso.

This will be simpler once this issue is implemented: https://code.google.com/p/android-test-kit/issues/detail?id=71

like image 79
yogurtearl Avatar answered Nov 15 '22 22:11

yogurtearl


I stumbled upon this question in my search for a similar answer. Using concepts from Stefano Dacchille's article on IdlingResources, I built the following idling resource that waits for a specific Activity to be active before firing. In my case, I know the dialog is showing when a fragment with a specific tag exists. This isn't the same as the OP's test, but the concepts should translate well.

public class BusyWhenFragmentExistsInActivityIdlingResource implements IdlingResource {
    private FragmentActivity activity = null;
    private final String fragmentTag;
    private ResourceCallback resourceCallback;
    private boolean wasIdleLastTime = true; // Start off as idle
    private final String name;
    // Need this strong reference because ActivityLifecycleMonitorRegistry won't hold one
    private final ActivityLifecycleCallback activityLifecycleCallback;

    public BusyWhenFragmentExistsInActivityIdlingResource(
            final Class<? extends FragmentActivity> clazz,
            final String fragmentTag
    ){
        name = BusyWhenFragmentExistsInActivityIdlingResource.class.getSimpleName()+" "+clazz.getSimpleName();
        this.fragmentTag = fragmentTag;
        activityLifecycleCallback = new ActivityLifecycleCallback() {
            @Override
            public void onActivityLifecycleChanged(Activity activity, Stage stage) {
                if (!FragmentActivity.class.isAssignableFrom(activity.getClass())) {
                    return;
                }
                FragmentActivity fragmentActivity = (FragmentActivity) activity;
                if (!clazz.isAssignableFrom(fragmentActivity.getClass())) {
                    return;
                }
                switch (stage){
                    case RESUMED:
                        BusyWhenFragmentExistsInActivityIdlingResource.this.activity = fragmentActivity;
                        break;
                    case STOPPED:
                        BusyWhenFragmentExistsInActivityIdlingResource.this.activity = null;
                        break;
                }
            }
        };

        ActivityLifecycleMonitorRegistry.getInstance()
                .addLifecycleCallback(activityLifecycleCallback);

    }
    @Override
    public String getName() {
        return name;
    }

    @Override
    public boolean isIdleNow() {
        if (activity==null) {
            return wasIdleLastTime = true;
        }
        boolean isIdleThisTime = activity
                .getSupportFragmentManager()
                .findFragmentByTag(fragmentTag)==null;

        if (!wasIdleLastTime && isIdleThisTime && resourceCallback!=null){
            resourceCallback.onTransitionToIdle();
        }
        return wasIdleLastTime = isIdleThisTime;
    }

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

To use it, add something similar to this to your test:

@Before
public void setUp() throws Exception {
    registerIdlingResources(new BusyWhenFragmentExistsInActivityIdlingResource(
            SomeOtherActivity.class,
            BaseActivity.LOADING_DIALOG
    ));
}
like image 25
JohnnyLambada Avatar answered Nov 15 '22 22:11

JohnnyLambada