Cambio de configuración con Robolectric
Para conservar mis AsyncTasks a través de los cambios de configuración, utilizo una solución basada en fragmentos con setRetainInstance (true), que aloja cada AsyncTask y llama de vuelta a una actividad de escucha, similar a esta solución http://www.androiddesignpatterns.com/2013/04 /retaining-objects-across-config-changes.html
En última instancia, el propósito es probar la funcionalidad de retención de AsyncTask durante los cambios de configuración usando Robolectric, pero necesito comenzar con la configuración del cambio de configuración real correctamente. Sin embargo, parece que no puedo imitar el comportamiento de referencia exacta que se produce durante un cambio de configuración.
- Prueba de una biblioteca de Android con Robolectric
- No se puede ejecutar la prueba Roboeléctrica
- Cómo simular un evento de arrastre en un ViewPager en Roboeléctrico?
- Configuración de Deckard para Gradle. Error con la prueba Robolectric
- Cómo utilizar findViewById () en robolectric
Aplicación real: al ejecutar una aplicación real, al cambiar la configuración, la actividad se destruye y se vuelve a crear mientras se mantiene el fragmento, por lo que parece estar funcionando. Puedo ver esto revisando sus referencias antes y después del cambio de configuración (ejemplos de referencias que se utilizan a continuación):
-
Real app, before: Actividad: abc Fragmento: xyz
-
Real app, after: Actividad: bca Fragmento: xyz (correctamente retenido y reatado)
Caso 1: Sin embargo, al ejecutar recreate () en la Actividad en la prueba Robolectric, la Actividad no parece tener su instancia correctamente recreada (a pesar de que los documentos dicen que el método realiza todas las llamadas del ciclo de vida):
mActivityController = Robolectric.buildActivity(AsyncTaskTestActivity.class).attach().create().start().resume().visible(); mActivity = mActivityController.get(); mActivity.recreate();
-
Robolectric con recreate (), antes: Actividad: abc Fragmento: xyz
-
Robolectric con recreate (), después de Actividad: abc Fragmento: xyz
Esto me lleva a creer que una nueva instancia de actividad no se crea correctamente y la funcionalidad de reinserción por lo tanto no ha ocurrido de una manera real.
Caso 2: Si creo la prueba basada en llamadas de ciclo de vida individuales en su lugar:
mActivityController = Robolectric.buildActivity(AsyncTaskTestActivity.class).attach().create().start().resume().visible(); mActivityController.pause().stop().destroy(); mActivityController = Robolectric.buildActivity(AsyncTaskTestActivity.class).attach().create().start().resume().visible();
En esta versión, parece que la Actividad se reemplaza completamente desde cero, pero también lo hace el Fragmento:
-
Roboelectric con llamadas de ciclo de vida separadas, antes de Actividad: abc Fragmento: xyz
-
Roboeléctrico con llamadas de ciclo de vida separadas, después de Actividad: bca Fragmento: yzx
Parece que estoy reutilizando la misma Actividad (caso 1) o reemplazando todo con nuevas instancias, como si no hay ninguna Aplicación subyacente que retiene el Fragmento (caso 2).
Pregunta: ¿hay alguna manera de configurar mi prueba Robolectric para imitar el resultado de referencia que obtengo al ejecutar la aplicación en un entorno Android real (según el resultado de la aplicación Real), o estoy atascado con la creación de una aplicación de prueba independiente O establecerse con pruebas funcionales Robotium? Intenté hacerlo como este https://stackoverflow.com/a/26468296 pero conseguí el mismo resultado que mi caso 2.
¡Gracias por adelantado!
- Robolectric Unit Test falla con las actualizaciones de Android Studio 2.3
- Android + Robolectric - RuntimeException / InstantiationException en queryBuilder.query () en ContentProvider
- Algo falta en mi proyecto de prueba de Android?
- Prueba de que el botón inicia una Actividad con Robolectric
- JaCoCo no trabaja con pruebas Robolectric
- Recursos de la biblioteca con Robolectric 3 - JodaTime
- ¿Es posible hacer peticiones http reales con robolectric
- ¿Cómo puedo analizar una suite de pruebas junit?
He jugado alrededor de un pedacito y he venido para arriba con una solución usando Robolectric 3.0 y Mockito:
@RunWith(RobolectricGradleTestRunner.class) @Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.KITKAT, shadows = {ExampleActivityTest.ExampleActivityShadow.class}) public class ExampleActivityTest { @Mock private FragmentManager fragmentManagerMock; @Before public void setup() { initMocks(this); setupFragmentManagerMock(); } @Test public void testRestoreAfterConfigurationChange() { // prepare ActivityController<ExampleActivity> controller = Robolectric.buildActivity(ExampleActivity.class); ExampleActivity activity = controller.get(); ExampleActivityShadow shadow = (ExampleActivityShadow) Shadows.shadowOf(activity); shadow.setFragmentManager(fragmentManagerMock); ActivityController<ExampleActivity> controller2 = Robolectric.buildActivity(ExampleActivity.class); ExampleActivity recreatedActivity = controller2.get(); ExampleActivityShadow recreatedActivityShadow = (ExampleActivityShadow) Shadows.shadowOf(recreatedActivity); recreatedActivityShadow.setFragmentManager(fragmentManagerMock); // run & verify controller.create().start().resume().visible(); activity.findViewById(R.id.inc_button).performClick(); activity.findViewById(R.id.inc_button).performClick(); assertEquals(2, activity.lostCount.count); assertEquals(2, activity.retainedCount.count); Bundle bundle = new Bundle(); controller.saveInstanceState(bundle).pause().stop().destroy(); controller2.create(bundle).start().restoreInstanceState(bundle).resume().visible(); assertEquals(0, recreatedActivity.lostCount.count); assertEquals(2, recreatedActivity.retainedCount.count); } private void setupFragmentManagerMock() { final HashMap<String, Fragment> fragments = new HashMap<>(); doAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return fragments.get(invocation.getArguments()[0]); } }).when(fragmentManagerMock).findFragmentByTag(anyString()); final HashMap<String, Fragment> fragmentsToBeAdded = new HashMap<>(); final FragmentTransaction fragmentTransactionMock = mock(FragmentTransaction.class); doAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { fragmentsToBeAdded.put((String) invocation.getArguments()[1], (Fragment) invocation.getArguments()[0]); return fragmentTransactionMock; } }).when(fragmentTransactionMock).add(any(Fragment.class), anyString()); doAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { fragments.putAll(fragmentsToBeAdded); return null; } }).when(fragmentTransactionMock).commit(); when(fragmentManagerMock.beginTransaction()).thenReturn(fragmentTransactionMock); } @Implements(Activity.class) public static class ExampleActivityShadow extends ShadowActivity { private FragmentManager fragmentManager; @Implementation public FragmentManager getFragmentManager() { return fragmentManager; } public void setFragmentManager(FragmentManager fragmentManager) { this.fragmentManager = fragmentManager; } } }
Tenga en cuenta que sólo me he burlado de los métodos de FragmentManager ( beginTransaction()
y findFragmentByTag()
) y FragmentTransaction ( add()
y commit()
) que utilizo en mi código, por lo que es posible que tenga que expandirlos dependiendo de su código.
No he hecho demasiado trabajo con Robolectric todavía, así que puede haber una solución más elegante a esto, pero esto trabaja para mí por ahora.
Puede ver el código fuente completo y la configuración del proyecto aquí: https://github.com/rgeldmacher/leash (podría valer la pena si todavía necesita retener objetos;))
- El método checkSelfPermission (Context, String) no está definido para el tipo ContextCompat
- Ejecutar aplicaciones en Genymotion a través de una red local