tutorial - Obtener actividad actual en espresso android
test ui android (10)
En el caso de una prueba que cruza múltiples actividades, ¿hay alguna manera de obtener actividad actual?
El método getActivtiy () simplemente da una actividad que se usó para comenzar la prueba.
Intenté algo como abajo,
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;
}
pero obtengo un objeto nulo
En Espresso, puede usar ActivityLifecycleMonitorRegistry
pero no es oficialmente compatible, por lo que puede no funcionar en versiones futuras.
Así es como funciona:
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];
}
La respuesta aceptada puede no funcionar en muchas pruebas de espresso. Lo siguiente funciona con espresso versión 2.2.2 y Android compile / target SDK 27 ejecutándose en dispositivos 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;
}
La solución propuesta por @lacton no funcionó para mí, probablemente porque la actividad no estaba en un estado que fue reportado por ActivityLifecycleMonitorRegistry
.
Incluso intenté Stage.PRE_ON_CREATE
todavía no Stage.PRE_ON_CREATE
ninguna actividad.
Nota : no pude usar ActivityTestRule
o IntentTestRule
porque estaba iniciando mi actividad usando activitiy-alias
y no tenía ningún sentido usar la clase real en las pruebas cuando quiero hacer una prueba para ver si el alias funciona.
Mi solución para esto fue suscribirme a los cambios del ciclo de vida a través de ActivityLifecycleMonitorRegistry
y bloquear el hilo de prueba hasta que se inicie la actividad:
// 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
es básicamente un objeto envoltorio. Puede usar una matriz o cualquier otra cosa para capturar un valor dentro de la clase anónima.
Me gusta la versión de @ Ryan porque no usa elementos internos no documentados, pero puedes escribir esto aún más corto:
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];
}
Tenga en cuenta que esto no funcionará al ejecutar sus pruebas en Firebase Test Lab. Que falla con
java.lang.ClassCastException: com.android.internal.policy.DecorContext cannot be cast to android.app.Activity
Mejoré la respuesta de @Fabian Streitel para que pueda utilizar este método sin 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]; }
No pude hacer funcionar ninguna de las otras soluciones, así que terminé teniendo que hacer esto:
Declare su ActivityTestRule
:
@Rule
public ActivityTestRule<MainActivity> mainActivityTestRule =
new ActivityTestRule<>(MainActivity.class);
Declare una matriz de actividad final
para almacenar sus actividades:
private final Activity[] currentActivity = new Activity[1];
Agregue un método auxiliar para registrarse con el contexto de la aplicación para obtener actualizaciones del ciclo de vida:
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) { }
});
}
Agrega un método de ayuda para obtener la actividad actual
private Activity getCurrentActivity() {
return currentActivity[0];
}
Entonces, una vez que haya lanzado su primera actividad, simplemente llame a monitorCurrentActivity()
y luego cuando necesite una referencia a la actividad actual, simplemente llame a getCurrentActivity()
Si tiene la única actividad en su caso de prueba, puede hacer:
1. declarar la Rule
prueba
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];
}
2. obtener tu Activity
:
@Rule
public ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>(TestActivity.class);
Eso es un pedazo de pastel!
Si todo lo que necesita es verificar la Activity
actual, el uso puede llevarse bien con el one-liner nativo de Espresso para verificar que se lanzó el intento esperado:
intended(hasComponent(new ComponentName(getTargetContext(), ExpectedActivity.class)));
Espresso también te mostrará los intentos disparados mientras tanto si no coinciden con los tuyos.
La única configuración que necesita es reemplazar ActivityTestRule
con IntentsTestRule
en la prueba para permitirle realizar un seguimiento de los intentos de lanzamiento. Y asegúrese de que esta biblioteca esté en sus dependencias build.gradle
:
androidTestCompile ''com.android.support.test.espresso:espresso-intents:2.2.1''
build.gradle
androidTestCompile(''com.android.support.test.espresso:espresso-core:2.2.2'',
{
exclude group: ''com.android.support'', module: ''support-annotations''
})
Y, en tu clase de prueba instrumentada
@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
}
mActivityTestRule.getActivity()