在Espresso android中获取当前活动

时间:2014-07-01 18:49:24

标签: android ui-automation android-espresso

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

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

我尝试了类似下面的内容,

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

但我得到了空对象。

12 个答案:

答案 0 :(得分: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'

答案 1 :(得分:26)

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

答案 2 :(得分:10)

我喜欢@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

答案 3 :(得分:5)

Android团队已将ActivityTestRule替换为ActivityScenario。我们可以使用activityTestRule.getActivity()ActivityTestRule,但不能使用ActivityScenario。这是我关于从ActivityScenario获取Activity的解决方案的工作(受@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 :(得分:4)

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

1。声明你测试Rule

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

2。得到你Activity

mActivityTestRule.getActivity()

那是一块馅饼!

答案 5 :(得分:3)

try-catch

答案 6 :(得分:3)

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

声明您的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()

答案 7 :(得分:1)

基于https://stackoverflow.com/a/50762439/6007104,这是用于访问当前活动的通用util的Kotlin版本:

ANTLR Tool version 4.5.3 used for code generation does not match the 
current runtime version 4.7.1ANTLR Runtime version 4.5.3 used for parser
compilation does not match the current runtime version 4.7.1ANTLR Tool 
version 4.5.3 used for code generation does not match the current runtime 
version 4.7.1ANTLR Runtime version 4.5.3 used for parser compilation does 
not match the current runtime version 4.7.1 
Cannot find
import com.example.app.databinding.FragmentMyBindingImpl;

然后像这样简单地使用:

class CurrentActivityDelegate(application: Application) {
    private var cachedActivity: Activity? = null

    init {
        monitorCurrentActivity(application)
    }

    fun getCurrentActivity() = cachedActivity

    private fun monitorCurrentActivity(application: Application) {
        application.registerActivityLifecycleCallbacks(
            object : Application.ActivityLifecycleCallbacks {
                override fun onActivityResumed(activity: Activity) {
                    cachedActivity = activity
                    Log.i(TAG, "Current activity updated: ${activity::class.simpleName}")
                }

                override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {}
                override fun onActivityStarted(activity: Activity?) {}
                override fun onActivityPaused(activity: Activity?) {}
                override fun onActivityStopped(activity: Activity?) {}
                override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {}
                override fun onActivityDestroyed(activity: Activity?) {}
            })
    }
}

答案 8 :(得分: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基本上是一个包装器对象。您可以使用数组或其他任何东西来捕获匿名类中的值。

答案 9 :(得分:0)

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

            1 

              82 

                [label_id] => 13  
                [label_id] => 15
                [label_id] => 85

              242

                 [label_id] => 40
                 [label_id] => 41

            0
               494

                  [label_id] => 84
                  [label_id] => 77

                  496

                     [label_id] => 43

答案 10 :(得分:-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
    }

答案 11 :(得分:-1)

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

<div class="wrap heightwrap wow fadeInDown" data-wow-delay="0.4s">
    <svg viewBox="0 0 1000 300" class="svg-defs">
    <!-- Symbol with Logo -->
      <symbol id="s-text2">
        <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="30%" y="-25%" width="700" height="800" viewBox="0 0 1000 800">
            <defs>
              <rect id="a" x="50%" y="50%" width="612" height="792"/>
            </defs>
            <clipPath id="b">
              <use href="#a" overflow="visible"/>
            </clipPath>
            <path clip-path="url(#b)" d="M468.896,118.65l-80.667,254.289H145.563l-24.334-62.667h188 c0,0,26.167,0.167,33.311-18.676l15.356-50.99c20.333-62.333,22.67-67.217-34-67.667c-15.121-0.12-30.689,0.122-39.254,0.072 c-17.878-0.105-22.246,0.139-20.368-10.094l10.712-34.979c0,0,1.285-9.287,17.66-9.287"/>
            <path clip-path="url(#b)" d="M275.563,267.9c28.667,0,28.013-1.13,35.833-28.5c8-28,9.457-29.082-12-29.5 c-10.548-0.205-26.166,0-26.166,0c-51.959,0-64.834,1.5-58.222-24.03c2.682-10.354,6.587-25.693,12.221-45.637 c4.024-14.247,8.103-29.353,11.369-38.006C250.481,70.74,271.842,75.708,343.23,75.9c92.334,0.249,139.333,0,139.333,0l20.333-62 h-314l-41.553,149.031c0,0-2.537,10.65-6.408-0.212L87.896,13.9h-69l89.667,254H275.563"/>
        </svg>
      </symbol>
    <!-- Mask with text -->
      <mask id="m-text2" maskunits="userSpaceOnUse" maskcontentunits="userSpaceOnUse">
        <rect width="100%" height="100%" class="mask__shape">
        </rect>
        <use href="#s-text2" class="mask__text"></use>
      </mask>
   </svg>

   <div class="box-with-text">
   <!-- Container for video -->
   <div class="text-fill">
   <video class="video" src="/css/fractal3.mp4" autoplay loop></video>
  </div>
  <!-- Visible SVG -->
   <svg viewBox="0 0 1000 300" class="svg-inverted-mask">
    <rect width="100%" height="100%" mask="url(#m-text2)" class="shape--fill" />
      <use href="#s-text2" class="text--transparent"></use>
    </svg>
   </div>
</div>