在Espresso android中获取当前活动

问题描述 投票:37回答:11

如果测试跨越多个活动,是否有办法获得当前活动?

getActivity()方法只提供一个用于启动测试的活动。

我试过下面的东西,

public Activity getCurrentActivity() {
    Activity activity = null;
    ActivityManager am = (ActivityManager) this.getActivity().getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
    try {
        Class<?> myClass = taskInfo.get(0).topActivity.getClass();
        activity = (Activity) myClass.newInstance();
    }
    catch (Exception e) {

    }
    return activity;
}

但我得到了空对象。

android ui-automation android-espresso
11个回答
27
投票

在Espresso中,您可以使用ActivityLifecycleMonitorRegistry但它不受官方支持,因此在将来的版本中可能无法使用。

下面是它的工作原理:

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];
}

-1
投票

的build.gradle

androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2',
                {
            exclude group: 'com.android.support', module: 'support-annotations'
        })

并且,在您的仪器化测试课程中

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

    MainActivity mainActivity;

    @Before
    public void setUp() throws Exception {
        mainActivity = intentsTestRule.getActivity(); //now Activity's view gets created
        //Because Activity's view creation happens in UI Thread, so all the test cases, here, are used by UI Thread
    }

-1
投票

接受的答案可能不适用于许多浓咖啡测试。以下适用于在API 25设备上运行的espresso版本2.2.2和Android编译/目标SDK 27:

@Nullable
private Activity getActivity() {
    Activity currentActivity = null;

    Collection resumedActivities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(RESUMED);
    if (resumedActivities.iterator().hasNext()){
        currentActivity = (Activity) resumedActivities.iterator().next();
    }
    return currentActivity;
}

27
投票

如果您只需要对当前的Activity进行检查,请使用可能与原生Espresso单行相关以检查是否已启动预期意图:

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

如果不匹配,Espresso也会向您显示同时触发的意图。

您需要的唯一设置是在测试中用ActivityTestRule替换IntentsTestRule,让它跟踪启动的意图。并确保此库位于您的build.gradle依赖项中:

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

11
投票

我喜欢@ Ryan的版本,因为它不使用无证书的内部,但你可以写得更短:

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];
}

请注意,虽然在Firebase测试实验室中运行测试时这不起作用。那失败了

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

7
投票

Android团队用ActivityTestRule取代了ActivityScenario。我们可以用activityTestRule.getActivity()ActivityTestRule而不用ActivityScenario。以下是我从Activity获取ActivityScenario的解决方案(灵感来自@Ryan和@Fabian解决方案)

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

4
投票

我无法使用任何其他解决方案,所以我最终不得不这样做:

宣布你的ActivityTestRule

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

声明一个final Activity数组来存储您的活动:

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

添加辅助方法以注册应用程序上下文以获取生命周期更新:

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) { }
            });
}

添加辅助方法以获取当前活动

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

所以,一旦你启动了你的第一个活动,只需要调用monitorCurrentActivity(),然后当你需要引用当前活动时,你只需要调用getCurrentActivity()


4
投票
public static Activity getActivity() {
    final Activity[] currentActivity = new Activity[1];
    Espresso.onView(AllOf.allOf(ViewMatchers.withId(android.R.id.content), isDisplayed())).perform(new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isAssignableFrom(View.class);
        }

        @Override
        public String getDescription() {
            return "getting text from a TextView";
        }

        @Override
        public void perform(UiController uiController, View view) {
            if (view.getContext() instanceof Activity) {
                Activity activity1 = ((Activity)view.getContext());
                currentActivity[0] = activity1;
            }
        }
    });
    return currentActivity[0];
}

2
投票

如果您的测试用例中只有Activity,则可以执行以下操作:

1.宣布你测试Rule

@Rule
public ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>(TestActivity.class);

给你Activity

mActivityTestRule.getActivity()

那是一块馅饼!


0
投票

@lacton提出的解决方案对我不起作用,可能是因为活动不在ActivityLifecycleMonitorRegistry报告的状态。

我甚至尝试过Stage.PRE_ON_CREATE仍然没有得到任何活动。

注意:我无法使用ActivityTestRuleIntentTestRule,因为我使用activitiy-alias开始我的活动,并且当我想测试以查看别名是否有效时,在测试中使用实际的类没有任何意义。

我的解决方案是通过ActivityLifecycleMonitorRegistry订阅生命周期更改并阻止测试线程直到活动启动:

// NOTE: make sure this is a strong reference (move up as a class field) otherwise will be GCed and you will not stably receive updates.
ActivityLifecycleCallback lifeCycleCallback = new ActivityLifecycleCallback() {
            @Override
            public void onActivityLifecycleChanged(Activity activity, Stage stage) {
                classHolder.setValue(((MyActivity) activity).getClass());

                // release the test thread
                lock.countDown();
            }
         };

// used to block the test thread until activity is launched
final CountDownLatch lock = new CountDownLatch(1);
final Holder<Class<? extends MyActivity>> classHolder = new Holder<>();
instrumentation.runOnMainSync(new Runnable() {
   @Override
    public void run() {
        ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(lifeCycleCallback);
     }
});

// start the Activity
intent.setClassName(context, MyApp.class.getPackage().getName() + ".MyActivityAlias");
context.startActivity(intent);
// wait for activity to start
lock.await();

// continue with the tests
assertTrue(classHolder.hasValue());
assertTrue(classHolder.getValue().isAssignableFrom(MyActivity.class));

Holder基本上是一个包装器对象。您可以使用数组或其他任何内容来捕获匿名类中的值。


0
投票

我改进了@Fabian Streitel答案,因此您可以在没有ClassCastException的情况下使用此方法

public static Activity getCurrentActivity() {
    final Activity[] activity = new Activity[1];

    onView(isRoot()).check((view, noViewFoundException) -> {

        View checkedView = view;

        while (checkedView instanceof ViewGroup && ((ViewGroup) checkedView).getChildCount() > 0) {

            checkedView = ((ViewGroup) checkedView).getChildAt(0);

            if (checkedView.getContext() instanceof Activity) {
                activity[0] = (Activity) checkedView.getContext();
                return;
            }
        }
    });
    return activity[0];
}
© www.soinside.com 2019 - 2024. All rights reserved.