43 votes

Unité testant un récepteur de diffusion?

Voici un BroadcastReceiver de mon projet, je suis à la recherche de l'unité de test. Lorsque l'utilisateur effectue un appel téléphonique, il saisit le numéro de téléphone, et met en place une intention de démarrer une nouvelle activité, en passant le numéro de téléphone.

public class OutgoingCallReceiver extends BroadcastReceiver 
{
    @Override
    public void onReceive(Context xiContext, Intent xiIntent) 
    {
        if (xiIntent.getAction().equalsIgnoreCase(Intent.ACTION_NEW_OUTGOING_CALL))
        {
            String phoneNum = xiIntent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);

            Intent intent = new Intent(xiContext, MyActivity.class);
            intent.putExtra("phoneNum", phoneNum);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

            xiContext.startActivity(intent);
            setResultData(null);
        }
    }
}

Jusqu'à présent, mon unité de test ressemble à ceci:

public class OutgoingCallReceiverTest extends AndroidTestCase
{
    private OutgoingCallReceiver mReceiver;

    @Override
    protected void setUp() throws Exception
    {
        super.setUp();

        mReceiver = new OutgoingCallReceiver();
    }

    public void testStartActivity()
    {
        Intent intent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
        intent.putExtra(Intent.EXTRA_PHONE_NUMBER, "01234567890");

        mReceiver.onReceive(getContext(), intent);        
    }
}

Cela va à travers le code, mais je veux que mon test pour être en mesure de vérifier que l'intention a été envoyé, et pour vérifier le numéro de téléphone sur elle. Comment dois-je faire?

Je peux également tester que l'appel est annulé (à cause de la setResultData(null) de la ligne)?

50voto

thomson_matt Points 3447

corlettk m'a pointé sur l'objet MockContext dans Android, qui fait l'affaire. J'en ai fait une sous-classe, TestContext, qui ressemble à ceci:

 public class TestContext extends MockContext
{
    private List<Intent> mReceivedIntents = new ArrayList<Intent>();

    @Override
    public String getPackageName()
    {
        return "com.mypackage.test";
    }

    @Override
    public void startActivity(Intent xiIntent)
    {
        mReceivedIntents.add(xiIntent);
    }

    public List<Intent> getReceivedIntents()
    {
        return mReceivedIntents;
    }
}
 

Et mon cas de test ressemble maintenant à ceci:

 public class OutgoingCallReceiverTest extends AndroidTestCase
{
    private OutgoingCallReceiver mReceiver;
    private TestContext mContext;

    @Override
    protected void setUp() throws Exception
    {
        super.setUp();

        mReceiver = new OutgoingCallReceiver();
        mContext = new TestContext();
    }

    public void testStartActivity()
    {
        Intent intent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
        intent.putExtra(Intent.EXTRA_PHONE_NUMBER, "01234567890");

        mReceiver.onReceive(mContext, intent);        
        assertEquals(1, mContext.getReceivedIntents().size());
        assertNull(mReceiver.getResultData());

        Intent receivedIntent = mContext.getReceivedIntents().get(0);
        assertNull(receivedIntent.getAction());
        assertEquals("01234567890", receivedIntent.getStringExtra("phoneNum"));
        assertTrue((receivedIntent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0);
    }
}
 

28voto

corlettk Points 5051

Matt,

Des sons comme vous avez besoin de mock-up, un Contexte ... et puis échanger vos méthodes à accepter des interfaces au lieu de classes concrètes: public void onReceive(IContext c, IIntent i), juste pour les fins de test. Mais ensuite, le Contexte et l'Intention des classes ne sont pas les vôtres sont-ils... ils sont Android... donc vous ne pouvez pas "juste" faire de la mise en oeuvre de vos interfaces, de sorte que vous auriez à "envelopper" afin d'exposer un de votre interface, ce qui est PLUTÔT beaucoup de code pour ne pas en gagner beaucoup. Très Dégoûtant!!!

J'ai donc commencé à me demander si quelqu'un a été à travers tout cela avant, et fait le plus dur verges pour nous... et tada: http://developer.android.com/reference/android/test/mock/package-summary.html

Des acclamations. Keith.

6voto

Pascal Zaugg Points 68

Depuis que cette question a été posée, les cadres railleurs ont évolué à peu près. Avec mockito, vous pouvez désormais vous moquer non seulement des interfaces mais aussi des classes. Je suggère donc de résoudre ce problème en se moquant d'un contexte et en utilisant ArgumentCapture:

 import static org.mockito.Mockito.*;

public class OutgoingCallReceiverTest extends AndroidTestCase {
    private OutgoingCallReceiver mReceiver;
    private Context mContext;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        //To make mockito work
        System.setProperty("dexmaker.dexcache", 
                mContext.getCacheDir().toString());

        mReceiver = new OutgoingCallReceiver();
        mContext = mock(Context.class);
    }

    public void testStartActivity() {
        Intent intent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
        intent.putExtra(Intent.EXTRA_PHONE_NUMBER, "01234567890");

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

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

        Intent receivedIntent = argument.getValue();         
        assertNull(receivedIntent.getAction());
        assertEquals("01234567890", receivedIntent.getStringExtra("phoneNum"));
        assertTrue((receivedIntent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0);
    }
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X