Search code examples
androidandroid-intentjunitmockingmockito

Creating intent in test: "Method putExtra in android.content.Intent not mocked"


I'm trying to unit test a broadcast receiver which listens for "com.android.music.metachanged" intents using JUnit4 and Mockito.

The broadcast receiver starts a service when it receives an intent. I want to assert that the service is started. I also want to assert that the string extra "artist" of the received intent is the same as the one of the sent intent.

This is what I have so far...

@RunWith(PowerMockRunner.class)
public class MusicBroadcastReceiverUnitTest {
    private MusicBroadcastReceiver mReceiver;

    @Mock
    private Context mContext;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        mReceiver = new MusicBroadcastReceiver();
    }

    @Test
    public void testStartMusicRegistrationService() {
        Intent intent = new Intent("com.android.music.metachanged");
        intent.putExtra("artist", "SampleArtist");

        mReceiver.onReceive(mContext, intent);
        assertNull(mReceiver.getResultData());

        ArgumentCaptor<Intent> argument = ArgumentCaptor.forClass(Intent.class);
        verify(mContext, times(1)).startService(argument.capture());

        Intent receivedIntent = argument.getValue();
        assertEquals("SampleArtist", receivedIntent.getStringExtra("artist"));
    }
}

But this fires a java.lang.RuntimeException: Method putExtra in android.content.Intent not mocked.

I checked this out, but I think the OP had a different problem, since they don't send out an intent from inside the test body.


Solution

  • All right, I took a look at Method of ContentValues is not mocked as suggested by @Jeff Bowman. Sadly, that question doesn't provide any code, so I hope this will be useful for somebody...

    @RunWith(PowerMockRunner.class)
    public class MusicBroadcastReceiverUnitTest {
        private MusicBroadcastReceiver mReceiver;
    
        @Mock
        private Context mContext;
    
        @Mock
        private Intent androidIntent;
    
        @Before
        public void setUp() {
            MockitoAnnotations.initMocks(this);
    
            mReceiver = new MusicBroadcastReceiver();
        }
    
        @Test
        public void testStartMusicRegistrationService() {
            try {
            PowerMockito.whenNew(Intent.class)
                   .withArguments(String.class).thenReturn(androidIntent);
            } catch (Exception e) {
                e.printStackTrace();
            }
            when(androidIntent.getAction())
              .thenReturn("com.android.music.metachanged");
            when(androidIntent.getStringExtra("artist"))
              .thenReturn("SampleArtist");
    
            mReceiver.onReceive(mContext, intent);
    
            ArgumentCaptor<Intent> argument = ArgumentCaptor.forClass(Intent.class);
            verify(mContext, times(1)).startService(argument.capture());
    
            Intent receivedIntent = argument.getValue();
            assertEquals("SampleArtist", receivedIntent.getStringExtra("artist"));
        }
    }
    

    So yeah, I rather mocked "getStringExtra" than "putExtra". But it worked for me.