Get Current Activity in Espresso android

Solution 1:

In Espresso, you can use ActivityLifecycleMonitorRegistry but it is not officially supported, so it may not work in future versions.

Here is how it works:

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

Solution 2:

If all you need is to make the check against current Activity, use may get along with native Espresso one-liner to check that expected intent was launched:

intended(hasComponent(new ComponentName(getTargetContext(), ExpectedActivity.class)));

Espresso will also show you the intents fired in the meanwhile if not matching yours.

The only setup you need is to replace ActivityTestRule with IntentsTestRule in the test to let it keep track of the intents launching. And make sure this library is in your build.gradle dependencies:

androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'

Solution 3:

I like @Ryan's version as it doesn't use undocumented internals, but you can write this even shorter:

private Activity getCurrentActivity() {
    final Activity[] activity = new Activity[1];
    onView(isRoot()).check(new ViewAssertion() {
        @Override
        public void check(View view, NoMatchingViewException noViewFoundException) {
            activity[0] = (Activity) view.getContext();
        }
    });
    return activity[0];
}

Please be aware, though that this will not work when running your tests in Firebase Test Lab. That fails with

java.lang.ClassCastException: com.android.internal.policy.DecorContext cannot be cast to android.app.Activity

Solution 4:

The Android team has replaced ActivityTestRule with ActivityScenario. We could do activityTestRule.getActivity() with ActivityTestRule but not with ActivityScenario. Here is my work around solution for getting an Activity from ActivityScenario (inspired by @Ryan and @Fabian solutions)

@get:Rule
var activityRule = ActivityScenarioRule(MainActivity::class.java)
...
private fun getActivity(): Activity? {
  var activity: Activity? = null
  activityRule.scenario.onActivity {
    activity = it
  }
  return activity
}

Solution 5:

I couldn't get any of the other solutions to work, so I ended up having to do this:

Declare your ActivityTestRule:

@Rule
public ActivityTestRule<MainActivity> mainActivityTestRule =
        new ActivityTestRule<>(MainActivity.class);

Declare a final Activity array to store your activities:

private final Activity[] currentActivity = new Activity[1];

Add a helper method to register with the application context to get lifecycle updates:

private void monitorCurrentActivity() {
    mainActivityTestRule.getActivity().getApplication()
            .registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
                @Override
                public void onActivityCreated(final Activity activity, final Bundle savedInstanceState) { }

                @Override
                public void onActivityStarted(final Activity activity) { }

                @Override
                public void onActivityResumed(final Activity activity) {
                    currentActivity[0] = activity;
                }

                @Override
                public void onActivityPaused(final Activity activity) { }

                @Override
                public void onActivityStopped(final Activity activity) { }

                @Override
                public void onActivitySaveInstanceState(final Activity activity, final Bundle outState) { }

                @Override
                public void onActivityDestroyed(final Activity activity) { }
            });
}

Add a helper method to get the current activity

private Activity getCurrentActivity() {
    return currentActivity[0];
}

So, once you've launched your first activity, just call monitorCurrentActivity() and then whenever you need a reference to the current activity you just call getCurrentActivity()