GetActivity () causa RuntimeException: No se pudo iniciar intención Intent act = android.intent.action.MAIN
Actualizado # 1: más información agregada al final de esta publicación
Soy nuevo en el desarrollo y las pruebas de Android.
- Android Espresso testing 'No se puede resolver el símbolo' InstrumentationRegistry ''
- Espresso Android que iguala las vistas de texto dentro del padre
- Prueba de errores de EditText con Espresso en Android
- ¿Es posible inhabilitar tostadas o esperar hasta que el tostado desaparezca mientras se prueba
- La prueba de unidad que incluye un Toast en el código de producción falla
Tengo tres exámenes Espresso. La primera prueba pasa, pero la segunda no se ejecutará porque la llamada a getActivity () en el método setUp () antes de la segunda prueba siempre falla:
Blockquote java.lang.RuntimeException: No se pudo iniciar intención Intent {act = android.intent.action.MAIN flg = 0x10000000 cmp = mi.nombre de archivo / .ActivityMain} en 45 segundos. …
La tercera prueba pasa.
No tengo operaciones largas de funcionamiento, animaciones o llamadas de red en la creación. Puedo hacer clic en todos los elementos del menú de mi aplicación repitiendo manualmente el flujo de prueba sin problemas.
Por alguna razón, la primera prueba "rompe" la próxima llamada getActivity () en setUp () antes de la segunda prueba. Todas las pruebas siguientes (después de la segunda prueba) correrán bien.
He encontrado una pregunta similar , pero parece que no se resolvió y tiene un problema un poco diferente.
Código de prueba:
import static com.google.android.apps.common.testing.ui.espresso.Espresso.onData; import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView; import static com.google.android.apps.common.testing.ui.espresso.Espresso.openActionBarOverflowOrOptionsMenu; import static com.google.android.apps.common.testing.ui.espresso.action.ViewActions.click; import static com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions.matches; import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.isDisplayed; import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId; import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import net.humblegames.bodylasticscalculator.ActivityMain; import net.humblegames.bodylasticscalculator.R; import net.humblegames.bodylasticscalculator.applogic.BandSystem; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.After; import org.junit.Before; import android.app.Activity; import android.test.ActivityInstrumentationTestCase2; import android.util.Log; public class MenuNavigationTestExperiment extends ActivityInstrumentationTestCase2<ActivityMain> { private String TAG = getClass().getSimpleName(); private Activity activity; public MenuNavigationTestExperiment() { super(ActivityMain.class); } @Before public void setUp() throws Exception { Log.d(TAG, "SETUP"); activity = getActivity(); super.setUp(); } @After public void tearDown() throws Exception { Log.d(TAG, "TEARDOWN"); super.tearDown(); } public void testFirst() { Log.d(TAG, "testFirst"); clickMenuItem("Select band system"); onData(allOf(is(instanceOf(BandSystem.class)), hasName("MMA Training"))) .onChildView(withId(R.id.label)).perform(click()); clickMenuItem("About"); onView(withId(R.id.about_webview)).check(matches(isDisplayed())); } public void testSecond() { Log.d(TAG, "testSecond"); // this test will not run } public void testThird() { Log.d(TAG, "testThird"); // this test will pass } // ------------------ HELPER METHODS --------------------------------------- private void clickMenuItem(String menuItem) { Log.d(TAG, "clickMenuItem"); openActionBarOverflowOrOptionsMenu(getInstrumentation() .getTargetContext()); onView(withText(menuItem)).perform(click()); } private Matcher<BandSystem> hasName(final String name) { Log.d(TAG, "hasName"); return new BaseMatcher<BandSystem>() { @Override public boolean matches(final Object item) { final BandSystem foo = (BandSystem) item; return name == foo.getName(); } @Override public void describeTo(final Description description) { description.appendText("getName should return ").appendValue( name); } }; } }
Rastreo de error:
java.lang.RuntimeException: Could not launch intent Intent { act=android.intent.action.MAIN flg=0x10000000 cmp=net.humblegames.bodylasticscalculator/.ActivityMain } within 45 seconds. Perhaps the main thread has not gone idle within a reasonable amount of time? There could be an animation or something constantly repainting the screen. Or the activity is doing network calls on creation? See the threaddump logs. For your reference the last time the event queue was idle before your activity launch request was 1395582828351 and and now the last time the queue went idle was: 1395582830169. If these numbers are the same your activity might be hogging the event queue. at com.google.android.apps.common.testing.testrunner.GoogleInstrumentation.startActivitySync(GoogleInstrumentation.java:277) at android.test.InstrumentationTestCase.launchActivityWithIntent(InstrumentationTestCase.java:119) at android.test.InstrumentationTestCase.launchActivity(InstrumentationTestCase.java:97) at android.test.ActivityInstrumentationTestCase2.getActivity(ActivityInstrumentationTestCase2.java:104) at net.humblegames.bodylasticscalculator.test.MenuNavigationTestExperiment.setUp(MenuNavigationTestExperiment.java:42) at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191) at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176) at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554) at com.google.android.apps.common.testing.testrunner.GoogleInstrumentationTestRunner.onStart(GoogleInstrumentationTestRunner.java:167) at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
Actualización # 1
Creé un proyecto de eclipse limpio y una prueba de Espresso limpia para ese proyecto. Pude reproducir el mismo error durante la ejecución de prueba de Espresso (pero todavía no estoy seguro si la causa de este error es la misma en mi aplicación real):
La aplicación de destino tiene 3 actividades (principal, segunda y tercera). El usuario navega entre ellos haciendo clic en los elementos del menú: la actividad principal comienza la segunda, la segunda inicia la tercera.
Encontré que si llamo clickMenuItem () (ver abajo) Y Thread.sleep (500) en la primera prueba esto causa falla en setUp () en getActivity () llamada antes de la segunda prueba. Si el tiempo de espera en el sueño es inferior a 0,5 segundos, no hay ningún fallo.
Al mismo tiempo, si llama Thread.sleep (10000) y no llama a clickMenuItem () no hay bloqueo también. Duerrá con éxito durante 10 segundos en la primera prueba y también pasará la segunda.
Código de prueba:
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> { private String TAG = getClass().getSimpleName(); private Activity activity; public MainActivityTest() { super(MainActivity.class); } public void setUp() throws Exception { Log.d(TAG, "SETUP"); activity = getActivity(); super.setUp(); } public void tearDown() throws Exception { Log.d(TAG, "TEARDOWN"); super.tearDown(); } public void testFirst() throws InterruptedException { Log.d(TAG, "testFirst"); clickMenuItem("Start second activity"); clickMenuItem("Start third activity"); Thread.sleep(500); } public void testSecond() { Log.d(TAG, "testSecond"); // this test will not run } private void clickMenuItem(String menuItem) { Log.d(TAG, "clickMenuItem"); openActionBarOverflowOrOptionsMenu(getInstrumentation() .getTargetContext()); onView(withText(menuItem)).perform(click()); } }
Código objetivo:
Actividad principal:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection switch (item.getItemId()) { case R.id.action_start_second_activity: Intent intent = new Intent(this, SecondActivity.class); startActivity(intent); return true; default: return super.onOptionsItemSelected(item); } } }
Segunda actividad:
public class SecondActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.second, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection switch (item.getItemId()) { case R.id.action_start_third_activity: Intent intent = new Intent(this, ThirdActivity.class); startActivity(intent); return true; default: return super.onOptionsItemSelected(item); } } }
Tercera Actividad:
public class ThirdActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_third); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.third, menu); return true; } }
- Haga clic en el elemento de menú que se encuentra a veces en el menú de desbordamiento
- Prueba de interfaz de usuario de Espresso para Android: falla en la ejecución de la prueba: Error en la ejecución de la instrumentación debido a 'java.lang.IllegalAccessError'
- ¿Por qué Espresso deja la aplicación una vez finalizada la prueba? Cómo detenerlo de hacer eso
- Cómo utilizar el espresso para presionar un botón AlertDialog
- Lanzamiento de pruebas Espresso en varios emuladores
- Cómo rotar la actividad, quiero decir: el cambio de orientación de la pantalla con Espresso?
- Espresso NestedScrollView
- Ejecución de un Espresso Multidexed .APK
Encontré una solución. He añadido el código que golpea el botón de nuevo varias veces en el método tearDown () para cerrar todas las actividades abiertas anteriormente. Desafortunadamente no encontré otro método para cerrar todas las actividades abiertas en Espresso. Robotium tiene un método muy conveniente para ese propósito solo.finishOpenedActivities ().
public void tearDown() throws Exception { Log.d(TAG, "TEARDOWN"); goBackN(); super.tearDown(); } private void goBackN() { final int N = 10; // how many times to hit back button try { for (int i = 0; i < N; i++) Espresso.pressBack(); } catch (com.google.android.apps.common.testing.ui.espresso.NoActivityResumedException e) { Log.e(TAG, "Closed all activities", e); } }
La solución pressBack no funcionó para mí, pero encontré otra:
@Override protected void tearDown() throws Exception { closeAllActivities(getInstrumentation()); super.tearDown(); } public static void closeAllActivities(Instrumentation instrumentation) throws Exception { final int NUMBER_OF_RETRIES = 100; int i = 0; while (closeActivity(instrumentation)) { if (i++ > NUMBER_OF_RETRIES) { throw new AssertionError("Limit of retries excesses"); } Thread.sleep(200); } } public static <X> X callOnMainSync(Instrumentation instrumentation, final Callable<X> callable) throws Exception { final AtomicReference<X> retAtomic = new AtomicReference<>(); final AtomicReference<Throwable> exceptionAtomic = new AtomicReference<>(); instrumentation.runOnMainSync(new Runnable() { @Override public void run() { try { retAtomic.set(callable.call()); } catch (Throwable e) { exceptionAtomic.set(e); } } }); final Throwable exception = exceptionAtomic.get(); if (exception != null) { Throwables.propagateIfInstanceOf(exception, Exception.class); Throwables.propagate(exception); } return retAtomic.get(); } public static Set<Activity> getActivitiesInStages(Stage... stages) { final Set<Activity> activities = Sets.newHashSet(); final ActivityLifecycleMonitor instance = ActivityLifecycleMonitorRegistry.getInstance(); for (Stage stage : stages) { final Collection<Activity> activitiesInStage = instance.getActivitiesInStage(stage); if (activitiesInStage != null) { activities.addAll(activitiesInStage); } } return activities; } private static boolean closeActivity(Instrumentation instrumentation) throws Exception { final Boolean activityClosed = callOnMainSync(instrumentation, new Callable<Boolean>() { @Override public Boolean call() throws Exception { final Set<Activity> activities = getActivitiesInStages(Stage.RESUMED, Stage.STARTED, Stage.PAUSED, Stage.STOPPED, Stage.CREATED); activities.removeAll(getActivitiesInStages(Stage.DESTROYED)); if (activities.size() > 0) { final Activity activity = activities.iterator().next(); activity.finish(); return true; } else { return false; } } }); if (activityClosed) { instrumentation.waitForIdleSync(); } return activityClosed; }
Esto parece funcionar para mí para cerrar todas las actividades en la pila, excepto la primera.
@After @Override public void tearDown() throws Exception { Intent intent = new Intent(getActivity(), getActivity().getClass()); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // Removes other Activities from stack intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); myActivity.startActivity(intent); super.tearDown(); }
Pruebe lo siguiente:
@Before public void setUp() throws Exception { super.setUp(); Log.d(TAG, "SETUP"); activity = getActivity(); }
Es trabajo para mí:
androidTestCompile ('com.android.support.test.espresso:espresso-contrib:2.2'){ exclude group: 'com.android.support', module: 'appcompat' exclude group: 'com.android.support', module: 'support-v4' exclude module: 'recyclerview-v7'
}
- ¿Cómo utilizar la nueva función de Android M de "Selección de texto" que se ofrece desde fuera de la aplicación?
- ¿Existe una etiqueta XML equivalente a `ListView.addHeaderView '?