Stubbing your Android Intents

Know your intent was sent without the Activity opening

Photo by marc thiele, used under a creative commons license. https://flic.kr/p/7aMoPV

In my previous post, I talked about how handy it is to use espresso intents to test which intents are sent in your Android application. I have since run into a new problem. In the application I’m working on we have a button that opens a link in the user’s browser. When this feature was added, we included a test to make sure our new button worked as expected.

Intents.init();
onView(withId(R.id.button)).perform(click());
Matcher expectedIntent = allOf(hasAction(Intent.ACTION_VIEW), hasData(correct_url));
intended(expectedIntent);
Intents.release();

This correctly tested that the browser was opened with the correct URL, but it made the rest of our test suite unhappy. It didn’t know what to do when an application other than ours was opened. Since we didn’t need to know anything other than that the browser intent was sent, we wondered if there was a way to keep it from opening while still testing what we needed to.

We discovered that Intents also has a method, intending(), that enables intent stubbing. When we stub an intent, we are able to intercept it so the intent is never sent to the system, and specify how it should respond. Through using this method, we were able to solve our issue with only one line of code.

intending(expectedIntent).respondWith(new Instrumentation.ActivityResult(0, null));

Notice that we also have a .respondWith(). This lets us specify the ActivityResult for the stubbed intent, and it needs to be included if the intent is going to be intercepted correctly. We don’t use any response for our example, so we just used empty data.

This intending() method doesn’t assert that the intent was sent, but when we combine it with the intended() check we had before, we have the exact behavior we are looking for in our test!

Intents.init();
Matcher expectedIntent = allOf(hasAction(Intent.ACTION_VIEW), hasData(correct_url));
intending(expectedIntent).respondWith(new Instrumentation.ActivityResult(0, null));
onView(withId(R.id.button)).perform(click());
intended(expectedIntent);
Intents.release();
Photo of Victoria Gonda

Victoria is a software developer working on mobile and full stack web applications. She enjoys exchanging knowledge through conference talks and writing.

Comments

  1. michiel.v.liempt@gmail.com
    Michiel
    February 16, 2017 at 14:38 PM

    Hi
    Thanks for the snippet.
    Just in case you have multiple of these tests and one fails in the on click, you might want to use try {} finally {} to make sure Intents.release() is called. Like this

            Intents.init();
            Matcher expectedIntent = allOf(hasAction(Intent.ACTION_VIEW), hasData(something);
            intending(expectedIntent).respondWith(new Instrumentation.ActivityResult(0, null));
    
            try {
                onView(withText(containsString(somethingElse))).perform(click());
                intended(expectedIntent);
            } finally {
                Intents.release();
            }
    </pre>