Cómo burlar getApplicationContext
Tengo una aplicación que almacena la información de contexto de la aplicación. La información de contexto de la aplicación se comparte entre las actividades de la clase MyApp que extiende la clase Application.
Estoy escribiendo una prueba de unidad para mi actividad, y quiero comprobar que cuando el usuario hace clic en un botón de la actividad, el estado de una aplicación cambiará. Algo como esto:
- ¿Es posible inyectar simulacros para realizar pruebas con AndroidAnnotations?
- Instrumentation.ActivityMonitor no monitoreando Intent.ACTION_CALL
- Cómo escribir una prueba de unidad para un controlador de excepción de subprocesos no detectados.
- Cómo probar interfaz de escucha se llama dentro de pruebas de unidad de Android
- Prueba de unidad con Junit: prueba de recursos de red / bluetooth
@Override public void onClick(View pView) { ((MyApp)getApplicationContext()).setNewState(); }
El problema es que no sé cómo burlarme del contexto de la aplicación. Estoy utilizando ActivityUnitTestCase como una base de caso de prueba. Cuando llamo a setApplication , cambia el valor del miembro de mApplication de la clase Activity , pero no del contexto de la aplicación. He intentado setActivityContext también, pero parece incorrecto (no es el contexto de la aplicación, pero el contexto de la actividad) y se dispara afirmar dentro startActivity ).
Así que la pregunta es – ¿Cómo burlar getApplicationContext () ?
- Uso de Mockito Matchers.any () con android.support.annotation.IntDef anotación personalizada
- ¿Cómo puedo ejecutar una única prueba de Android con Kotlin?
- Android Robolectric unidad de prueba de Marshmallow PermissionHelper
- ¿Por qué fallan las grandes pruebas de unidad de actividad de Android?
- Robolectric InflateException al usar el diseño de barra de acción personalizada
- Proyecto de biblioteca de prueba autónoma no puede encontrar las clases de biblioteca
- ¿Por qué AndroidTestCase.getContext (). GetApplicationContext () devuelve null?
- AndroidManifest en el directorio androidTest que se ignora
Dado que el método getApplicationContext
está dentro de la clase que está extendiendo, se vuelve algo problemático. Hay un par de problemas a considerar:
- Usted realmente no puede burlarse de una clase que está bajo prueba, que es uno de los muchos inconvenientes con la herencia de objetos (es decir, subclase).
- El otro problema es que
ApplicationContext
es un singleton , lo que hace que todo sea más malo para probar, ya que no se puede burlar fácilmente de un estado global que está programado para ser insustituible.
Lo que puede hacer en esta situación es preferir la composición de objetos sobre la herencia . Así que para hacer que su Activity
comprobable, necesita dividir un poco la lógica. Digamos que su Activity
se llama MyActivity
. Tiene que estar compuesto de un componente lógico (o clase), lo que se MyActivityLogic
. Aquí está una figura simple del diagrama de la clase:
Para resolver el problema singleton, dejamos que la lógica sea "inyectada" con un contexto de aplicación, por lo que se puede probar con un simulacro. Entonces sólo necesitamos probar que el objeto MyActivity
ha puesto el contexto de la aplicación correcta en MyActivityLogic
. ¿Cómo básicamente resolver ambos problemas es a través de otra capa de abstracción (parafraseado de Butler Lampson). La nueva capa que agregamos en este caso es la actividad lógica movida fuera del objeto de actividad.
Por el bien de su ejemplo, las clases tienen que verse así:
public final class MyActivityLogic { private MyApp mMyApp; public MyActivityLogic(MyApp pMyApp) { mMyApp = pMyApp; } public MyApp getMyApp() { return mMyApp; } public void onClick(View pView) { getMyApp().setNewState(); } } public final class MyActivity extends Activity { // The activity logic is in mLogic private final MyActivityLogic mLogic; // Logic is created in constructor public MyActivity() { super(); mLogic = new MyActivityLogic( (MyApp) getApplicationContext()); } // Getter, you could make a setter as well, but I leave // that as an exercise for you public MyActivityLogic getMyActivityLogic() { return mLogic; } // The method to be tested public void onClick(View pView) { mLogic.onClick(pView); } // Surely you have other code here... }
Todo debe ser algo como esto:
Para probar MyActivityLogic
sólo necesitará un jUnit TestCase
simple en lugar de ActivityUnitTestCase
(ya que no es una actividad), y puede burlarse de su contexto de aplicación utilizando su marco de simulación de elección (ya que el manejo de sus propios simulacros es un poco de una fricción ). Ejemplo utiliza Mockito :
MyActivityLogic mLogic; // The CUT, Component Under Test MyApplication mMyApplication; // Will be mocked protected void setUp() { // Create the mock using mockito. mMyApplication = mock(MyApplication.class); // "Inject" the mock into the CUT mLogic = new MyActivityLogic(mMyApplication); } public void testOnClickShouldSetNewStateOnAppContext() { // Test composed of the three A's // ARRANGE: Most stuff is already done in setUp // ACT: Do the test by calling the logic mLogic.onClick(null); // ASSERT: Make sure the application.setNewState is called verify(mMyApplication).setNewState(); }
Para probar MyActivity
se utiliza ActivityUnitTestCase
como de costumbre, solo necesitamos asegurarnos de que crea un MyActivityLogic
con el ApplicationContext
correcto. Ejemplo de código de prueba incompleto que hace todo esto:
// ARRANGE: MyActivity vMyActivity = getActivity(); MyApp expectedAppContext = vMyActivity.getApplicationContext(); // ACT: // No need to "act" much since MyActivityLogic object is created in the // constructor of the activity MyActivityLogic vLogic = vMyActivity.getMyActivityLogic(); // ASSERT: Make sure the same ApplicationContext singleton is inside // the MyActivityLogic object MyApp actualAppContext = vLogic.getMyApp(); assertSame(expectedAppContext, actualAppContext);
Espero que todo tenga sentido para ti y te ayude.
- Android AppCompat v21 proporciona SwitchCompat no proporciona SwitchCompatPerefrence
- Cómo apoyar todas las diferentes resoluciones de productos android