2015-04-08 61 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),但模拟接收器中的计数器为0.我已经在onReceive方法中设置了一个断点,并且它从未命中。我还添加了日志记录,并没有在logcat中显示。所以我100%确定接收器没有被调用。我也尝试增加线程中的睡眠时间,因为setInexactRepeating非常不精确,但我可以等待很长时间,但它仍然没有被调用。

我也尝试注册它在测试项目的清单,而不是编程方式,结果是相同的。

为什么接收机不被呼叫?


UPDATE
我可以确认AlarmManager不是问题。根据adb dumpsys警报正确注册警报。

我现在试图通过调用sendBroadcast来让接收器运行,但我处于死路一条。接收器不会被叫。我尝试了主要的应用上下文,测试用例上下文,甚至是ActivityInstrumentationTestCase2。试过还添加了WakeLocks而没有任何东西。没有办法让它被调用。我认为这可能是由意图或意图过滤器中的一些标志引起的(android似乎对标志非常挑剔)。

回答

0

所以答案是肯定的,但有可能,但接收者只能使用隐式(基于操作)的Intents和IntentFilters。

显式意图(基于类,没有过滤器)不适用于动态注册的接收器。为了明确的意图工作,您需要将接收器注册到清单中,而不是像我试图做的那样动态地注册。

这是Android中最黑暗且记录最少的功能之一。根据我的意图经验,你永远无法确定。而且它也很难诊断,因为logcat中没有警告可以帮助理解问题。荣誉@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

在Android源代码有一个alarmManagerTest与一个BroadcastReceiver执行alarmManager.setInexactRepeating。

主要区别在于工作android测试延迟15分钟,而您的测试使用1分钟延迟。

AlarmManager Android的文件说:在报警的后续重复之间毫秒

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

尽管不是正确的答案,但赏金是当之无愧的,因为您链接的测试向我展示了与我的代码唯一的区别是使用了隐式意图和意图过滤器。 –

相关问题