Obtenez L'activité actuelle dans Espresso android
dans le cas d'un test qui croise plusieurs activités, y a-t-il un moyen d'obtenir une activité courante?
La méthodegetActivtiy () ne donne qu'une 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 j'obtiens l'objet null.
10 réponses
dans Espresso, vous pouvez utiliser ActivityLifecycleMonitorRegistry
mais il n'est pas officiellement pris en charge, de sorte qu'il peut ne pas fonctionner dans les versions futures.
Voici comment ça marche:
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];
}
si tout ce que vous avez besoin est de faire la vérification par rapport à Activity
courant, l'utilisation peut s'entendre avec Espresso une doublure pour vérifier que l'intention prévue a été lancé:
intended(hasComponent(new ComponentName(getTargetContext(), ExpectedActivity.class)));
Espresso vous montrera également les intentions tirées en attendant si ne correspondant pas à la vôtre.
la seule configuration dont vous avez besoin est de remplacer ActivityTestRule
par IntentsTestRule
dans le test pour lui permettre de garder la trace des intentions de lancement. Et assurez-vous que cette bibliothèque est à votre build.gradle
dépendances:
androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.1'
j'aime la version de @Ryan car elle n'utilise pas de documents internes, mais vous pouvez é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];
}
s'il vous plaît soyez conscient, bien que cela ne fonctionnera pas lorsque vous effectuez vos tests dans Firebase Test Lab. Qui échoue avec
java.lang.ClassCastException: com.android.internal.policy.DecorContext cannot be cast to android.app.Activity
si vous avez la seule activité dans votre cas d'essai, vous pouvez faire:
1. déclarez-vous test Rule
@Rule
public ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>(TestActivity.class);
2. obtenez vous Activity
:
mActivityTestRule.getActivity()
C'est un morceau de tarte!
public static Activity getActivity() {
final Activity[] currentActivity = new Activity[1];
onView(allOf(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];
}
Les accepté de réponse peut ne pas fonctionner dans de nombreux espresso tests. Ce qui suit fonctionne avec la version 2.2.2 d'espresso et Android compiler/target SDK 27 tournant sur les appareils API 25:
@Nullable
private Activity getActivity() {
Activity currentActivity = null;
Collection resumedActivities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(RESUMED);
if (resumedActivities.iterator().hasNext()){
currentActivity = (Activity) resumedActivities.iterator().next();
}
return currentActivity;
}
Je n'ai pu trouver aucune autre solution, alors j'ai fini par avoir à le faire:
Déclarer votre ActivityTestRule
:
@Rule
public ActivityTestRule<MainActivity> mainActivityTestRule =
new ActivityTestRule<>(MainActivity.class);
Déclarer final
Activité tableau pour stocker vos activités:
private final Activity[] currentActivity = new Activity[1];
ajouter une méthode helper pour s'enregistrer avec le contexte de l'application pour 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 helper pour obtenir l'activité courante
private Activity getCurrentActivity() {
return currentActivity[0];
}
ainsi, une fois que vous avez lancé votre première activité, il suffit d'appeler monitorCurrentActivity()
et ensuite chaque fois que vous avez besoin d'une référence à l'activité actuelle, vous appelez simplement getCurrentActivity()
"151930920 de construire".Grad
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2',
{
exclude group: 'com.android.support', module: 'support-annotations'
})
et, dans votre classe D'essai instrumentée
@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
}
proposée par @lacton n'a pas fonctionné pour moi, probablement parce que l'activité n'était pas dans un État qui a été rapporté par ActivityLifecycleMonitorRegistry
.
j'ai même essayé Stage.PRE_ON_CREATE
toujours pas d'activité.
Note : Je ne pouvais pas utiliser le ActivityTestRule
ou IntentTestRule
parce que je commençais mon activité en utilisant activitiy-alias
et ne faisait aucun sens d'utiliser la classe réelle dans les tests quand je veux tester pour voir si l'alias travail.
ma solution à ce problème était de souscrire à des modifications du cycle de vie par le biais de ActivityLifecycleMonitorRegistry
et de bloquer le thread de test jusqu'à ce que l'activité soit lancée:
// 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 enveloppant. Vous pouvez utiliser un tableau ou n'importe quoi d'autre pour capturer une valeur dans la classe anonymous.
j'ai amélioré la réponse de @Fabian Streitel pour 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];
}