2015-04-08 3 views
10

Я хочу протестировать внутри единичного теста, был ли запущен сигнал тревоги, запрограммированный с помощью AlarmManager, и если да, то если он срабатывает в течение правильного периода.Возможно ли зарегистрировать приемник в тестовом примере?

Вот класс приемника для тестирования. Я создал его в своем тестовом проекте. (Примечание: это не зарегистрирован в манифесте)

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; 
    } 
} 

А вот тест на блок. Метод programReceiver фактически принадлежит классу основного проекта, но я включил его внутри теста, так что вам не нужно читать столько кода.

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 
     ); 
    } 
} 

Когда я выполнить метод test, приемник должен быть зарегистрирован динамически в setUp. Затем я программирую один и тот же сигнал тревоги дважды. Мое намерение состояло в том, чтобы проверить, что только последний сигнал остался активным, но мне не удается вообще вызвать приемник. Тест завершается неудачно, поскольку ожидается, что он будет вызываться один раз (или, по крайней мере, несколько раз> = 1), но счетчик в mock-приемнике равен 0. Я установил точку останова в методе onReceive, и он никогда не попадает , Я также добавил регистрацию и ничего не отображается в logcat. Поэтому я на 100% уверен, что приемник не вызван. Я также попытался увеличить время сна в потоке, потому что setInexactRepeating срабатывает очень неточно, но я могу ждать веков, и он все еще не называется.

Я также попытался зарегистрировать его в манифесте тестового проекта, а не программно, и результаты остались прежними.

Почему приемник не называется?


UPDATE
Я могу подтвердить AlarmManager не проблема. Аварийные сигналы правильно регистрируются в соответствии с аварийным сигналом adb dumpsys.

Теперь я пытаюсь запустить приемник, позвонив по телефону sendBroadcast, но я в тупике. Приемник просто не будет вызван. Я пробовал основной контекст приложения, контекст тестового случая, даже ActivityInstrumentationTestCase2. Пробовал также добавлять WakeLocks и ничего. Просто нет способа заставить его позвонить. Я думаю, что это может быть вызвано некоторыми флагами в фильтре намерения или намерения (андроид, похоже, очень разборчив с флагами).

ответ

0

Итак, да, это возможно, НО приемники будут работать только с неявными (основанными на действии) намерениями и IntentFilters.

Явные намерения (на основе классов, без фильтров) не работают с динамически зарегистрированными приемниками. Чтобы явные намерения работать, вам необходимо зарегистрировать приемник в манифесте, а не динамически, как я пытался.

Это одна из самых темных и менее документированных функций Android. По моему опыту с намерениями вы никогда не сможете быть уверенными. И это также очень сложно диагностировать, так как в логарифме нет предупреждений, которые могли бы помочь понять проблему. Кудос на ответ @ d2vid здесь: https://stackoverflow.com/a/19482865

Поэтому, чтобы исправить мой код, мне пришлось бы добавить элемент приемника в тег приложения в манифесте. Поскольку приемник - это класс прикомандированного приемника, созданный внутри тестового проекта, мне придется отредактировать манифест тестового проекта, а не основной проект. Но, с другой стороны, в тестовых проектах Eclipse приемные теги, добавленные в манифест, не работают. Android Studio лучше подходит для слияния манифеста, но это устаревший проект, запущенный в Eclipse, и мы его не переносим.

В заключение: явное тестирование намерений нарушено в Eclipse для тех приемников, которые не существуют в основном проекте. Класс, который мне нужен для тестирования, использует только явные намерения, поэтому мой тест не может быть записан в Eclipse.

0

Я не уверен, почему ваш тест работает не так, как ожидалось. Несколько предложений приходят на ум:

  1. Большая часть doco, которую я видел, например. [1], предполагает, что Object.wait() всегда следует вызывать в цикле на основе условия, которое не выполняется. Ваш код этого не делает. Возможно, вы могли бы попробовать переделать его так, чтобы он это делал.
  2. Для полноты, возможно, вы должны вывести что-то для InterruptedException, которое может быть выброшено flag.wait() на всякий случай.

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

+0

Хотя я согласен, что код синхронизации может быть лучше, он будет работать в 99,99% случаев, и это нормально для грязного модульного теста. –

2

В андроиде исходного кода есть alarmManagerTest, который выполняет alarmManager.setInexactRepeating с BroadcastReceiver.

Главное отличие заключается в том, что рабочий тест на Android имеет задержку в 15 минут, в то время как ваш тест использует задержку в 1 минуту.

Андроида документация AlarmManager говорит:

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

... intervalMillis интервал в миллисекундах между последующими повторами сигнала. До API 19, если это один из INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_HALF_DAY или INTERVAL_DAY, тогда сигнал тревоги будет выровнен по фазе с другими аварийными сигналами, чтобы уменьшить количество пробуждений. В противном случае будильник будет установлен так, как если бы приложение вызывало setRepeating (int, long, long, PendingIntent). Начиная с API 19, все повторяющиеся сигналы тревоги будут неточными и подлежат пакетной обработке с другими аварийными сигналами независимо от их указанного интервала повторения.

Я не уверен, если это означает, что разрешено использовать только многократные 15 минут.

На моем телефоне Android 2.2 неточный таймер работает только в том случае, если он представляет собой комбинацию из 15 минут.

+0

AlarmManager не является проблемой. На более низком уровне есть что-то, что не позволяет вызвать приемник. Прочитайте мое обновление. –

+0

Несмотря на отсутствие правильного ответа, щедрость заслуживает заслуги, поскольку связанный с ним тест показал, что единственное отличие от моего кода заключалось в использовании неявных намерений и целевых фильтров. –

Смежные вопросы