web-dev-qa-db-fra.com

Obtenir l'activité actuelle dans Espresso android

Dans le cas d'un test qui traverse plusieurs activités, existe-t-il un moyen d'obtenir l'activité actuelle?

la méthode getActivtiy () ne donne qu'une seule activité qui a été utilisée pour démarrer le test.

J'ai essayé quelque chose comme ci-dessous,

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

mais je reçois un objet nul.

38
fenrigne123

Dans Espresso, vous pouvez utiliser ActivityLifecycleMonitorRegistry mais il n'est pas officiellement pris en charge, il peut donc ne pas fonctionner dans les futures versions.

Voici comment cela fonctionne:

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

Si tout ce dont vous avez besoin est de faire la vérification par rapport au Activity actuel, vous pouvez vous en sortir avec une doublure Espresso native pour vérifier que l'intention attendue a été lancée:

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

Espresso vous montrera également les intentions tirées entre-temps si elles ne correspondent pas aux vôtres.

La seule configuration dont vous avez besoin est de remplacer ActivityTestRule par IntentsTestRule dans le test pour lui permettre de suivre les intentions de lancement. Et assurez-vous que cette bibliothèque se trouve dans votre build.gradle dépendances:

androidTestCompile 'com.Android.support.test.espresso:espresso-intents:2.2.1'
27
riwnodennyk

J'aime la version de @ Ryan car elle n'utilise pas de documents internes non documentés, mais vous pouvez l'écrire encore plus court:

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

Veuillez noter que cela ne fonctionnera pas lors de l'exécution de vos tests dans Firebase Test Lab. Cela échoue avec

Java.lang.ClassCastException: com.Android.internal.policy.DecorContext cannot be cast to Android.app.Activity
11
Fabian Streitel

L'équipe Android a remplacé ActivityTestRule par ActivityScenario . Nous pourrions faire activityTestRule.getActivity() avec ActivityTestRule mais pas avec ActivityScenario . Voici ma solution de contournement pour obtenir un Activity de ActivityScenario (inspiré des solutions @Ryan et @Fabian)

@get:Rule
var activityRule = ActivityScenarioRule(MainActivity::class.Java)
...
private fun getActivity(): Activity? {
  var activity: Activity? = null
  activityRule.scenario.onActivity {
    activity = it
  }
  return activity
}
7
Tanin
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];
}
4
Ryan

Je ne pouvais pas faire fonctionner les autres solutions, j'ai donc dû faire ceci:

Déclarez votre ActivityTestRule:

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

Déclarez un final tableau d'activités pour stocker vos activités:

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

Ajoutez une méthode d'assistance pour vous inscrire au contexte de l'application afin d'obtenir des mises à jour du cycle de vie:

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

Ajouter une méthode d'assistance pour obtenir l'activité actuelle

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

Donc, une fois que vous avez lancé votre première activité, il suffit d'appeler monitorCurrentActivity() puis chaque fois que vous avez besoin d'une référence à l'activité en cours, vous appelez simplement getCurrentActivity()

4
Billy Brawner

Si vous avez la seule activité dans votre scénario de test, vous pouvez faire:

1. déclarez que vous testez Rule

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

2. obtenez-vous Activity:

mActivityTestRule.getActivity()

C'est un morceau de tarte!

2
Slava

J'ai amélioré la réponse @Fabian Streitel afin que vous puissiez utiliser cette méthode sans 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];
}
0
Artem M

La solution proposée par @lacton n'a pas fonctionné pour moi, probablement parce que l'activité n'était pas dans un état signalé par ActivityLifecycleMonitorRegistry.

J'ai même essayé Stage.PRE_ON_CREATE n'a toujours eu aucune activité.

Note: Je ne pouvais pas utiliser le ActivityTestRule ou IntentTestRule parce que je commençais mon activité en utilisant activitiy-alias et cela n'avait aucun sens d'utiliser la classe réelle dans les tests quand je veux tester pour voir si l'alias fonctionne.

Ma solution à cela était de m'abonner aux modifications du cycle de vie via ActivityLifecycleMonitorRegistry et de bloquer le thread de test jusqu'au lancement de l'activité:

// 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 est essentiellement un objet wrapper. Vous pouvez utiliser un tableau ou toute autre chose pour capturer une valeur à l'intérieur de la classe anonyme.

0
Bakhshi