This is going to be a really basic post as it’ll only be about a small thing I learned about Android testing. And I’m a total noob at this point when it comes to writing tests for Android applications.
One of my client projects has a milestone where I have to provide automated tests for the codebase. It’s a weird milestone really, because how do you verify that you’ve completed the deliverables? Test coverage percentage? Actually, how does the client even verify that I wrote valid and useful tests? At any rate…
On the positive side, this milestone has forced me to finally start writing automated tests for my Android projects. I write tests instinctively for my Rails projects. Perhaps it’s because testing is so hard-baked into the Rails community’s culture, but I’ve never really written any automated tests before on my Android projects.
Since the app is almost at completion stage, I decided to write end to end integration tests using Espresso. For anyone coming from the Ruby on Rails world, it’s basically Capybara but for Android testing.
In one of the tests, I wanted to assert whether clicking on a button resulted in the app launching a specific activity. You “could” technically search for a text that you expect in the new screen, but since the data in the following screen after clicking a button is coming from an actual API, and the data-set is unpredictable (yes-yes… I should be mocking the data, but the client wanted me to hit the API for the tests), I wanted to check for the resulting Activity instead.
Before we start, you’ll have to include the Espresso Intent package in your app’s build.gradle
if you haven’t.
1 2 3 |
dependencies { androidTestCompile("com.android.support.test.espresso:espresso-intents:2.2.2") } |
Just replace that “2.2.2” in espresso-intents:2.2.2
with your Espresso’s version number.
Continuing on… Usually, in Espresso tests, you would be defining an activity rule like this
1 2 3 4 5 |
@RunWith(AndroidJUnit4.class) public class MainActivityTest { @Rule public ActivityTestRule mActivityTestRule = new ActivityTestRule<>(MainActivity.class); } |
And to test that a resulting activity has been launched, your test will look something like
1 2 3 4 5 6 7 8 9 10 11 |
@RunWith(AndroidJUnit4.class) public class MainActivityTest { @Rule public ActivityTestRule mActivityTestRule = new ActivityTestRule<>(MainActivity.class); @Test public void testThatItWorks() { onView(withId(R.id.button)).perform(click()); intended(hasComponent(ResultingActivity.class.getName())); } } |
Sounds reasonable right? Unfortunately, this will result in a NullPointerException
error. To fix this, you have to replace your ActivityTestRule
with the IntentsTestRule
like this
1 2 3 4 5 6 7 8 9 10 11 |
@RunWith(AndroidJUnit4.class) public class MainActivityTest { @Rule public IntentsTestRule mIntentsTestRule = new IntentsTestRule<>(MainActivity.class); @Test public void testThatItWorks() { onView(withId(R.id.button)).perform(click()); intended(hasComponent(ResultingActivity.class.getName())); } } |
After that, you should see your tests pass. What’s the difference between ActivityTestRule
and IntentsTestRule
? IntentsTestRule
is an extension/subclass of ActivityTestRule
and initializes Espresso intents before each test and releases them after each test. And that allows your Espresso tests to verify resulting activity class names and such, or any assertions that involves intents.