Me falta algo aquí. El JavaDoc de ActivityUnitTestCase
sugiere que este caso de prueba pruebe una Actividad aisladamente del sistema:Cómo evitar que ActivityUnitTestCase llame a Application.onCreate?
Esta clase proporciona pruebas aisladas de una sola actividad. La actividad bajo prueba se creará con una conexión mínima a la infraestructura del sistema, y usted puede inyectar versiones engañadas o envueltas de muchas de las dependencias de Activity.
Estaba asumiendo que incluye no iniciar realmente la aplicación. Además, expone un auxiliar setApplication
que uno puede presumiblemente utilizar para inyectar una aplicación simulada.
Sin embargo, cualquier ActivityUnitTestCase
que inicie inicia la aplicación (real) y llama a su método onCreate
. Más precisamente, el InstrumentationTestRunner
parece estar haciendo eso, y hacerlo incluso antes de que tenga la oportunidad de setApplication
en el método de prueba setUp
! No lo noté durante bastante tiempo, ya que parece suceder en un punto durante el lanzamiento de la suite de pruebas donde ni siquiera se alcanzan los puntos de interrupción de Eclipse, pero escribir en los registros en onCreate
revela que realmente se llama.
Esto me supera por completo. ¿Por qué querría usar un objeto de aplicación simulada cuando el corredor de prueba de Android ejemplifica y ejecuta la aplicación real de todos modos? Esto es aún más problemático teniendo en cuenta que el corredor de instrumentación se ejecuta en su propio hilo, y genera el hilo principal de la aplicación al hacerlo. Esto significa que hay una condición de carrera entre la ejecución de la prueba y el llamado al Application.onCreate
. Si haces algo allí que pueda afectar tus pruebas, p. escribiendo en un archivo de preferencias compartido, entonces estás completamente jodido, ya que tus pruebas fallarán al azar.
¿Me falta algo o es simplemente una gran supervisión en el marco de prueba?
ACTUALIZACIÓN Esto también parece afectar ApplicationTestCase
. Antes de que mi caso de prueba se haya iniciado, puedo alcanzar un punto de interrupción en mi clase de aplicación 'onCreate
. Comenzamos un Fire-and-forget AsyncTask allí, que fallará al azar porque no tengo ninguna posibilidad de burlarlo (recuerde, eso es antes de que se llame al setUp
en mi caso de prueba). Aquí está el seguimiento de la pila veo durante este oscuro invocación de onCreate:
Thread [<1> main] (Suspended (breakpoint at line 86 in QypeRadar))
QypeRadar.onCreate() line: 86
InstrumentationTestRunner(Instrumentation).callApplicationOnCreate(Application) line: 969
ActivityThread.handleBindApplication(ActivityThread$AppBindData) line: 4244
ActivityThread.access$3000(ActivityThread, ActivityThread$AppBindData) line: 125
ActivityThread$H.handleMessage(Message) line: 2071
ActivityThread$H(Handler).dispatchMessage(Message) line: 99
Looper.loop() line: 123
ActivityThread.main(String[]) line: 4627
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]
Method.invoke(Object, Object...) line: 521
ZygoteInit$MethodAndArgsCaller.run() line: 868
ZygoteInit.main(String[]) line: 626
NativeStart.main(String[]) line: not available [native method]
¿Por qué el corredor de prueba callApplicationOnCreate
a pesar de que the docs claramente:
el caso de prueba no llamará onCreate() hasta que su llamadas de prueba createApplication(). Esto le da la oportunidad de configurar o ajustar cualquier marco adicional o lógica de prueba antes de onCreate().
Eso es totalmente rotundo, ¡no me da la oportunidad!
Hasta donde puedo decir, solo hay dos maneras de solucionar esto: 1) reescribir InstrumentationTestRunner para no hacer esa llamada a 'callApplicationOnCreate' y dejarla en el caso de prueba para decidir si eso es necesario o no. Sin embargo, no estoy seguro del efecto que tendrá en otras pruebas. 2) Reescriba nuestro método onCreate para que sea libre de efectos secundarios e idempotente (recuerde que se llamará dos veces, ya que para que el caso de prueba funcione también debe llamar a 'createApplication'). ¿Pensamientos? – Matthias
Por lo tanto, he creado un corredor de prueba personalizado que elude 'callApplicationOnCreate', ahora todo funciona como se espera para las pruebas de * unidad *. Para las pruebas funcionales, aún necesitaría el corredor de prueba predeterminado que realiza un lanzamiento completo, por lo que ahora tengo dos corredores de prueba, lo cual es un poco molesto. – Matthias
Relato interesante. ¿Sería lo suficientemente simple pasar por alto 'callApplicationOnCreate' solo para los tipos de casos de prueba apropiados, lo que le permite crear un parche para el corredor de prueba que puede enviar en sentido ascendente? –