Voglio testare all'interno di un test dell'unità se viene attivato o meno un allarme programmato utilizzando AlarmManager
e, in caso affermativo, se viene attivato entro il periodo corretto.È possibile registrare un ricevitore in un caso di test?
Ecco la classe del ricevitore da testare. L'ho creato all'interno del mio progetto di test. (NOTA: non è registrata nel manifesto)
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;
}
}
Ed ecco la prova di unità. Il metodo programReceiver
appartiene effettivamente a una classe nel progetto principale, ma l'ho incluso all'interno del test in modo che non sia necessario leggere così tanto codice.
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
);
}
}
Quando eseguo il metodo test
, il ricevitore avrebbe dovuto essere registrato dinamicamente nel setUp
. Quindi programmo lo stesso allarme due volte. Il mio intento era di verificare che solo l'ultimo allarme rimanesse attivo, ma ho problemi a far chiamare il ricevitore. Il test fallisce in quanto dovrebbe essere chiamato una volta (o almeno un numero di volte> = 1), ma il contatore nel ricevitore fittizio è 0. Ho impostato un punto di interruzione nel metodo onReceive
e non viene mai colpito . Ho anche aggiunto la registrazione e nulla viene mostrato nel logcat. Quindi sono sicuro al 100% che il ricevitore non venga chiamato. Ho anche provato ad aumentare il tempo di sospensione nel thread, perché setInexactRepeating
si attiva in modo inesatto, ma posso aspettare per anni e non viene ancora chiamato.
Ho anche provato a registrarlo nel manifest del progetto di test anziché a livello di programmazione e i risultati sono gli stessi.
Perché il ricevitore non viene chiamato?
UPDATE
posso confermare il AlarmManager
non è il problema. Gli allarmi sono registrati correttamente in base all'allarme dump di adb.
Ora sto provando a far funzionare il ricevitore chiamando sendBroadcast
, ma sono in un vicolo cieco. Il ricevitore non verrà chiamato. Ho provato il contesto dell'app principale, il contesto del test case, anche ActivityInstrumentationTestCase2
. Ho provato anche ad aggiungere WakeLock e niente. Non c'è proprio modo di farlo chiamare. Penso che questo potrebbe essere causato da alcune bandiere nel filtro intent o intent (Android sembra essere davvero pignolo con le bandiere).
Mentre sono d'accordo sul fatto che il codice di sincronizzazione potrebbe essere migliore, funzionerà nel 99,99% dei casi ed è ok per un test dell'unità sporca. –