ActivityTestRule: Espresso’s Test “Lifecycle”

The purpose of this post is to shed some light onto the order of operations for test cases written using Espresso’s new ActivityTestRule. Specifically, discussing when methods like beforeActivityLaunched(), afterActivityLaunched(), and afterActivityFinished() are called relative to both the test and activity lifecycle.

History Lesson: ActivityInstrumentationTestCase2

First, the old way of instrumentation testing, ActivityInstrumentationTestCase2. In previous iterations of Espresso, 2.0 and earlier, a test case might look something like this:

With the important takeaways being:

  • All the ‘set up’ is done in the onSetUp method (Mock services, mock data, custom intents).
  • The method getActivity must be called to launch the Activity under test.
  • Writing a new test case for the same activity with a different setup often requires a new test class.

In addition, the test “lifecycle” is reasonably well defined:
ActivityInstrumentationLifecycle (1)

Saying there is a well defined test “lifecycle” is valid because:

  1.  onSetUp will always be called before getActivity()
  2. getActivity() will always come before the activity creation
  3. Activity creation will always come before test execution
  4. and etc… per above diagram

And it’s an important history lesson because the same assumptions can not be made for the new ActivityTestRule. In fact, several new rules, some of which are not entirely obvious, apply. Therefore, it becomes helpful to understand the differences described below before migrating legacy tests to the new ActivityTestRule structure.

The ActivityTestRule “Lifecycle”

In a successful effort to reduce ‘boilerplate’ (setup code) in test classes the Espresso developers introduced the ActivityTestRule. As a result, methods like onSetUp and getActivity were removed from test classes and brought into the new ActivityTestRule class. Allowing developers to write tests like,

Where MyCustomRule handles all the test set up for the class:

And as long as test cases use the default constructor for ActivityTestRule the test lifecycle is almost identical to before:

SideBySide

It is important to note:

  • @Before comes after Activity creation and therefore is probably not a good time to initialize all mocks.
  • The Activity will always be launched before test code begins executing.
  • It is not pictured in the above diagram but the Activity is still in onResume when @After method is executed.

ActivityTestRule: launchActivity=false;

Here is where things get interesting, ActivityTestRule’s third constructor allows developers the option to explicitly launch an activity per test case:

public ActivityTestRule(Class activityClass, boolean initialTouchMode, boolean launchActivity) {

By Passing false into the third parameter developers can easily write tests like:

A powerful option for developers but does it impact the test “lifecycle”? And if so does it matter?
The answer is yes. and maybe.

The below diagram shows the difference between launchActivity=true (the default), and launchActivity=false:

launchActivityComparison

What changed?

  • @Before no longer comes after activity creation. In fact, it now comes before any other operation.
  • The activity isn’t launched until the test code makes the call to ActivityTestRule.launchActivity().

What has not changed?

  • Test and activity teardown.
  • beforeActivityLaunched(), activity creation, and afterActivityLaunched() are still in the same order.

Key Takeaways

The first takeaway is the Espresso developers are liars:

“The activity under test will be launched before each test annotated with @Test and before any method annotated with @Before. It will be terminated after the test is completed and all methods annotated with @After are finished.” –Espresso Docs

Just kidding, kind of 😛

The activity will be launched before @Test and @Before is only true for the default case, where launchActivity=true. And it is an important distinction because developers can not rely on test set up functions to prepare mocks for the activity under test. The better alternative is to prepare the activity under test in the beforeActivityLaunched() method. beforeActivityLaunched() is guaranteed to be called before activity creation regardless of launchActivity.

The second takeaway, is to be careful migrating existing tests over to the new ActivityTestRule. It can be very easy just to migrate existing onSetUp(), tearDown() methods to the very similar @Before, @After structure. That said, the right thing to do is fully embrace the new ActivityTestRule and use the exposed methods, beforeActivityLaunched and afterActivityFinished(), appropriately. BECAUSE they have guaranteed behavior in relation to the Activity lifecycle.

Finally on an unrelated note, be careful when using the new IntentsTestRule. It does not initialize, Intents.init(), until after the activity is launched (afterActivityLaunched()). Meaning if an activity tries to trigger an intent in any of its’ lifecycle methods, onCreate, onStart, or onResume, the Espresso intent framework will miss it and it will be impossible to validate or stub those intents.

Thanks for reading! Please feel free to provide feedback in the comments below or find me on twitter:

I hope you found something of value in this post.

Advertisements

4 thoughts on “ActivityTestRule: Espresso’s Test “Lifecycle”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s