¿Es posible registrar un receptor en un caso de prueba?

Quiero probar dentro de una prueba de unidad si se dispara o no una alarma programada usando AlarmManager , y si es así, si se dispara dentro del período correcto.

Aquí está la clase del receptor a ser probada. Lo he creado dentro de mi proyecto de prueba. (NOTA: no está registrado en el manifiesto)

 public class MockBroadcastReceiver extends BroadcastReceiver { private static int numTimesCalled = 0; MockBroadcastReceiver(){ numTimesCalled = 0; } @Override public void onReceive(Context context, Intent intent) { numTimesCalled++; } public static int getNumTimesCalled() { return numTimesCalled; } public static void setNumTimesCalled(int numTimesCalled) { MockBroadcastReceiver.numTimesCalled = numTimesCalled; } } 

Y aquí está la prueba de unidad. El método programReceiver realidad pertenece a una clase en el proyecto principal, pero lo he incluido dentro de la prueba para que no tenga que leer tanto código.

 public class ATest extends AndroidTestCase { MockBroadcastReceiver mockReceiver; @Override protected void setUp() throws Exception { mockReceiver = new MockBroadcastReceiver(); getContext().registerReceiver(mockReceiver, new IntentFilter()); } @Override protected void tearDown() { getContext().unregisterReceiver(mockReceiver); mockReceiver = null; } public void test(){ //We're going to program twice and check that only the last //programmed alarm should remain active. final Object flag = new Object(); MockBroadcastReceiver.setNumTimesCalled(0); new Thread (){ @Override public void run(){ programReceiver(getContext(), MockBroadcastReceiver.class, 60000, 60000); SystemClock.sleep(20000); programReceiver(getContext(), MockBroadcastReceiver.class, 60000, 60000); SystemClock.sleep(90000); synchronized(flag){ flag.notifyAll(); } } }.start(); synchronized(flag){ try { flag.wait(); } catch (InterruptedException e) { } } assertEquals(1, MockBroadcastReceiver.getNumTimesCalled()); //should have been called at least once, but its 0. } private static void programReceiver(Context context, Class<? extends BroadcastReceiver> receiverClass, long initialDelay, long period){ Intent intent = new Intent(context, receiverClass); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); alarmManager.cancel(pendingIntent); //Cancel any previous alarm alarmManager.setInexactRepeating ( AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + initialDelay, period, pendingIntent ); } } 

Cuando ejecuto el método de test , el receptor debería haber sido registrado dinámicamente en el setUp . Entonces programo la misma alarma dos veces. Mi intención era probar que sólo la última alarma permanecía activa, pero estoy teniendo problemas para que el receptor se llame en absoluto. La prueba falla, ya que se espera que se llame una vez (o al menos un número de veces> = 1), pero el contador en el receptor simulador es 0. He establecido un punto de interrupción en el método onReceive y nunca se golpea . También he añadido el registro y nada muestra en el logcat. Así que estoy 100% seguro de que el receptor no está siendo llamado. También he intentado aumentar el tiempo de sueño en el hilo, porque setInexactRepeating dispara muy inexactamente, pero puedo esperar por años y todavía no se llama.

También he intentado registrarlo en el manifiesto del proyecto de prueba en lugar de programaticamente y los resultados son los mismos.

¿Por qué no se le llama al receptor?


ACTUALIZAR
Puedo confirmar que el AlarmManager no es el problema . Las alarmas se registran correctamente de acuerdo con la alarma adb dumpsys.

Ahora estoy tratando de que el receptor funcione llamando a sendBroadcast , pero estoy en un callejón sin salida. El receptor no será llamado. He intentado el contexto de la aplicación principal, el contexto del caso de prueba, incluso ActivityInstrumentationTestCase2 . Trató de añadir WakeLocks y nada. Simplemente no hay manera de que se llame. Creo que esto podría ser causado por algunas banderas en el intento o intención filtro (android parece ser muy exigente con banderas).

En el código fuente de Android hay un alarmManagerTest que ejecuta alarmManager.setInexactRepeating con un broadcastreceiver.

La principal diferencia es que la prueba androide de trabajo tiene un retraso de 15 minutos, mientras que la prueba utiliza un minuto de retraso.

La documentación de Android para AlarmManager dice:

  public void setInexactRepeating (int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) 

… intervalMillis intervalo en milisegundos entre repeticiones posteriores de la alarma. Antes de la API 19, si ésta es una de INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_HALF_DAY o INTERVAL_DAY, la alarma se alineará en fase con otras alarmas para reducir el número de reactivaciones. De lo contrario, la alarma se establecerá como si la aplicación hubiera llamado setRepeating (int, long, long, PendingIntent). A partir de la API 19, todas las alarmas repetitivas serán inexactas y sujetas a dosificación con otras alarmas independientemente del intervalo de repetición indicado.

No estoy seguro si esto significa que sólo se permiten multiplos de 15 minutos.

En mi teléfono Android 2.2 el temporizador inexacto sólo funciona si es un multible de 15 minutos.

No estoy seguro de por qué su prueba no funciona como se esperaba. Un par de sugerencias vienen a la mente:

  1. La mayoría de los doco que he visto, por ejemplo. [1], sugiere que Object.wait() siempre se debe llamar dentro de un bucle basado en una condición que no se mantiene. Tu código no lo hace. Tal vez podría intentar volver a trabajar para que lo haga.
  2. Para completar, tal vez debería producir algo para la flag.wait() InterruptedException que puede ser lanzada por flag.wait() si acaso.

[1] https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait–

Así que la respuesta es sí, es posible, pero los receptores sólo funcionan con implícitos (basados ​​en acciones) Intents e IntentFilters.

Las intenciones explícitas (basadas en la clase, sin filtros) no funcionan con los receptores registrados dinámicamente. Para intentos explícitos de trabajar, necesitas registrar el receptor en el manifiesto, y no dinámicamente como estaba tratando de hacer.

Esta es una de las características más oscuras y menos documentadas de Android. En mi experiencia con las intenciones, nunca se puede estar seguro. Y también es muy difícil de diagnosticar, ya que no hay ninguna advertencia impresa en el logcat que podría ayudar a entender el problema. Felicitaciones a la respuesta de @ d2vid aquí: https://stackoverflow.com/a/19482865

Así que para arreglar mi código tendría que añadir el elemento receptor dentro de la etiqueta de aplicación en el manifiesto. Como el receptor es una clase simulada de receptor creada dentro del proyecto de prueba, tendría que editar el manifiesto del proyecto de prueba, no el proyecto principal. Pero por otro lado, en los proyectos de prueba de Eclipse, las etiquetas de receptor añadidas al manifiesto no parecen funcionar. Android Studio parece más adecuado para la fusión de manifiesto, pero este es un proyecto heredado que se inicia en Eclipse y no lo estamos portando.

En conclusión: la prueba de intenciones explícitas se rompe en Eclipse para aquellos receptores que no existen en el proyecto principal. La clase que necesitaba probar utiliza sólo intentos explícitos para que mi prueba no se pueda escribir en Eclipse.

  • Cómo crear un módulo de prueba para probar una biblioteca de Android en IntelliJ IDEA
  • './gradlew -Dtest.single = SimpleTest test' ejecuta todas las pruebas que tengo
  • Prueba de unidad de android: borrar prefs antes de la actividad de prueba
  • ¿Puede Android ServiceTestCase <MyService> enviar mensajes a mi servicio?
  • ¿Cuál es el propósito de las anotaciones de @SmallTest, @MediumTest y @LargeTest en Android?
  • Android Gradle dos suites de pruebas de instrumentación
  • Uso de una carpeta de recursos en el proyecto de prueba para datos de cadena de prueba
  • Prueba FPS en la aplicación Android
  • Probar la base de datos en Android: ProviderTestCase2 o RenamingDelegatingContext?
  • Cómo instalar la aplicación sin firmar Android en el dispositivo?
  • Android Espresso Testing: ActivityTestRule no cierra la actividad
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.