如何在BroadcastReceiver中测试对PendingIntent.getBroadcast(...)的静态调用

时间:2013-11-16 15:24:10

标签: android unit-testing broadcastreceiver

我真的迷失在这个问题上。我确实构建了以下BroadcastReceiver

package ch.pas.smslistenerservice.receiver;

import ch.pas.smslistenerservice.service.SMSListenerService;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class Resend extends BroadcastReceiver {
    private static String logTag = "ch.pas.smslistenerservice.resend";

    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) { return; }

        Log.i(logTag, "Resend received");

        PendingIntent pIntent = getPendingIntent(context);
        if(pIntent != null) { 
            Log.d(logTag, "Canceling intent");
            pIntent.cancel(); 
        }

        Log.d(logTag, "Starting service...");
        Intent serviceIntent = new Intent(context, SMSListenerService.class);
        serviceIntent.putExtra(SMSListenerService.EXTRA_TYPE, SMSListenerService.TYPE_RESEND);
        context.startService(serviceIntent);
    }

    protected PendingIntent getPendingIntent(Context context) {
        return PendingIntent.getBroadcast(context, 0, 
                new Intent("ch.pas.smslistenerservice.RESEND"), 
                PendingIntent.FLAG_NO_CREATE);
    }
}

我尝试测试if语句,我很想测试PendingIntent对象是否获得取消消息。

if(pIntent != null) { 
    Log.d(logTag, "Canceling intent");
    pIntent.cancel(); 
 }

我尝试的是:

  1. 将检索挂起意图的方法分解出来,然后使用mockito模拟返回的PendingIntent。这没有用,因为Mockito不能模拟最终的类(如PendingIntent)。
  2. 使用PowerMock模拟静态调用PendingIntent.getBroadcast(...),但这没有用,因为PowerMock无法在Android上运行,如果我在JVM上运行此测试,我会得到一个带有“Stub!”的RuntimeException。因为我正在扩展仅适用于Android的BroadcastReceiver。
  3. 我知道在这种情况下测试它没有多大意义,因为在if语句中没有太多的逻辑可以测试,但是如果还有更多呢?

1 个答案:

答案 0 :(得分:3)

通过使用PowerMock,Mockito和Robolectric的组合解决了这个问题(< =这是缺失的变量)。使用Robolectric来阴影(模拟)对PendingIntent.getBroadcast(...)的静态调用并注入一个模拟的PendingIntent。

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import org.powermock.api.mockito.PowerMockito;
import org.robolectric.*;
import org.robolectric.annotation.Config;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;

import ch.pas.smslistenerservice.receiver.Resend;
import ch.pas.smslistenerservice.service.SMSListenerService;

@Config(shadows=CustomShadowPendingIntent.class)
@RunWith(RobolectricTestRunner.class)
public class ResendTest {
    Resend receiver;
    Context context;
    PendingIntent pendingIntent;

    @Before
    public void before() {
        context = Mockito.mock(Context.class);
        pendingIntent = PowerMockito.mock(PendingIntent.class);
        receiver = new Resend();
    }

    @Test
    public void testStartServiceWithoutExistingPendingIntent() {
        CustomShadowPendingIntent.setPendingIntent(null);

        Intent intent = new Intent("ch.pas.smslistenerservice.RESEND");
        receiver.onReceive(context, intent);      

        ArgumentCaptor<Intent> argument = ArgumentCaptor.forClass(Intent.class);
        verify(context).startService(argument.capture());
        Intent capturedIntent = argument.getValue();
        assertFalse(-1 == capturedIntent.getIntExtra(SMSListenerService.EXTRA_TYPE, -1));
        assertEquals(SMSListenerService.TYPE_RESEND, 
                capturedIntent.getIntExtra(SMSListenerService.EXTRA_TYPE, -1));
    }

    @Test
    public void testStartServiceWithExistingPendingIntent() {
        CustomShadowPendingIntent.setPendingIntent(pendingIntent);

        Intent intent = new Intent("ch.pas.smslistenerservice.RESEND");
        receiver.onReceive(context, intent);      

        verify(pendingIntent, times(1)).cancel();

        ArgumentCaptor<Intent> argument = ArgumentCaptor.forClass(Intent.class);
        verify(context).startService(argument.capture());
        Intent capturedIntent = argument.getValue();
        assertFalse(-1 == capturedIntent.getIntExtra(SMSListenerService.EXTRA_TYPE, -1));
        assertEquals(SMSListenerService.TYPE_RESEND, 
                capturedIntent.getIntExtra(SMSListenerService.EXTRA_TYPE, -1));
    }
}

阴影PendingIntent:

import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;

import android.app.PendingIntent;

@Implements(PendingIntent.class)
public class CustomShadowPendingIntent 
    extends org.robolectric.shadows.ShadowPendingIntent {
    private static PendingIntent pendingIntentMock;

    public static void setPendingIntent(PendingIntent intent) {
        pendingIntentMock = intent;
    }

    @Implementation
    public static PendingIntent getBroadcast(android.content.Context context, 
            int requestCode, android.content.Intent intent, int flags) {
        return pendingIntentMock;
    }
}