Test your app's activities

Activities serve as containers for every user interaction within your app, so it's important to test how your app's activities behave during device-level events, such as the following:

  • Another app, such as the device's phone app, interrupts your app's activity.
  • The system destroys and recreates your activity.
  • The user places your activity in a new windowing environment, such as picture-in-picture (PIP) or multi-window.

In particular, it's important to ensure that your activity behaves correctly in response to the events described in Understanding the Activity Lifecycle.

This guide describes how to evaluate your app's ability to maintain data integrity and a good user experience as your app's activities transition through different states in their lifecycles.

Drive an activity's state

One key aspect of testing your app's activities involves placing your app's activities in particular states. To define this "given" part of your tests, use instances of ActivityScenario, part of the AndroidX Test library. By using this class, you can place your activity in states that simulate the device-level events described at the beginning of this page.

ActivityScenario is a cross-platform API that you can use in local unit tests and on-device integration tests alike. On a real or virtual device, ActivityScenario provides thread safety, synchronizing events between your test's instrumentation thread and the thread that runs your activity under test. The API is also particularly well-suited for evaluating how an activity under test behaves when it's destroyed or created.

Caution: The ActivityScenario API is currently in beta. The public interface is finalized, but its behavior could change. To report an issue, submit a bug report to the Android issue tracker.

If your tests depend on stable activity behavior, use the ActivityTestRule class instead.

This section presents the most common use cases associated with this API.

Create an activity

To create the activity under test, add the code shown in the following snippet:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        val scenario = ActivityScenario.launch(MyActivity::class.java)
    }
}

After creating the activity, ActivityScenario transitions the activity to the RESUMED state. This state indicates that your activity is running and is visible to users. In this state, you're free to interact with your activity's View elements using Espresso UI tests.

Drive the activity to a new state

To drive the activity to a different state, such as CREATED or STARTED, call moveToState(). This action simulates a situation where your activity is stopped or paused, respectively, because it's interrupted by another app or a system action.

An example usage of moveToState() appears in the following code snippet:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        val scenario = ActivityScenario.launch(MyActivity::class.java)
        scenario.moveToState(State.CREATED)
    }
}

Recreate the activity

If a device is low on resources, the system might destroy an activity, requiring your app to recreate that activity when the user returns to your app. To simulate these conditions, call recreate():

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        val scenario = ActivityScenario.launch(MyActivity::class.java)
        scenario.recreate()
    }
}

The ActivityScenario class maintains the activity's saved instance state and any objects annotated using @NonConfigurationInstance. These objects are loaded into the new instance of your activity under test.

Trigger actions in the activity

All methods within ActivityScenario are blocking calls, so the API requires you to run them in the instrumentation thread.

To trigger actions in your activity under test, use Espresso view matchers to interact with elements in your view:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        val scenario = ActivityScenario.launch(MyActivity::class.java)
        onView(withId(R.id.refresh))
                .perform(click())
    }
}

If you need to call a method on the activity itself, however, you can do so safely by implementing ActivityAction:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEvent() {
        val scenario = ActivityScenario.launch(MyActivity::class.java)
        scenario.onActivity(object : ActivityAction<MyActivity>) {
            fun perform(activity: MyActivity) {
                activity.handleSwipeToRefresh()
            }
        }
    }
}

To learn more about how threading works in Android tests, see Understand threads in tests.