Introduction
When working on Espresso tests, you might have run into a situation where you need to verify what your app does when an activity is in a specific Lifecycle state. In this tutorial, we will learn how to achieve this by using the ActivityScenario class.
Goals
At the end of the tutorial, you would have learned:
- How to drive an Activity’s lifecycle.
Tools Required
- Android Studio. The version used in this tutorial is Android Studio Dolphin | 2021.3.1.
Prerequisite Knowledge
- Basic Android.
- Basic Espresso.
Project Setup
To follow along with the tutorial, perform the steps below:
-
Create a new Android project with the default Empty Activity.
-
Replace the entire content of the MainActivity.kt class with the code below. This is just a simple Activity that logs some text at
onCreate()
,onStart()
,onResume()
, andonDestroy()
methods.private const val TAG = "MAIN_ACTIVITY" class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { Log.d(TAG, "On Create") super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } override fun onStart() { Log.d(TAG, "On Start") super.onStart() } override fun onResume() { Log.d(TAG, "On Resume") super.onResume() } override fun onDestroy() { Log.d(TAG, "On Destroy") super.onDestroy() } }
-
In Android Studio’s Logcat, we can use the query below to see the log messages after we run our tests.
level:debug tag:MAIN_ACTIVITY
-
In the androidTest source set, remove all test cases from the file named ExampleInstrumentedTest.java.
Use ActivityScenario to Drive Activity States
The two primary methods in the ActivityScenario class that we need to be concerned of for this tutorial is the static factory method launch()
and the instance method moveToState()
.
- The
launch()
methods are used to create the ActivityScenario<A> objects, while the A generic type represents the type of the Activity being driven. - The
moveToState()
method is used to move the underlying Activity to a different state. The only states that it can move the Activity to areCREATED
,STARTED
,RESUMED
, andDESTROYED
.
As a side note, ActivityScenario also implements Closeable. You are recommended to close the Activity after the test completes, so you are recommended to use a Java try-with-resource
block or a Kotlin use {}
block to automatically close ActivityScenario.
Now it’s time to create our first test. Add the test below into the ExampleInstrumentedTest class.
@Test
fun lifecycleTest(){
ActivityScenario.launch(MainActivity::class.java).use { scenario ->
}
}
In the code snippet above, I used the simplest version of launch()
, which only requires the Class object of the Activity being tested. The scenario
parameter is of type ActivityScenario<MainActivity>. Using the scenario
parameter, we can now call moveToState()
methods to move the Activity to different states.
@Test
fun lifecycleTest(){
ActivityScenario.launch(MainActivity::class.java).use { scenario ->
scenario.moveToState(Lifecycle.State.CREATED)
scenario.moveToState(Lifecycle.State.STARTED)
scenario.moveToState(Lifecycle.State.RESUMED)
scenario.moveToState(Lifecycle.State.RESUMED)
scenario.moveToState(Lifecycle.State.RESUMED)
scenario.moveToState(Lifecycle.State.DESTROYED)
}
}
After running this test, we can see the code in our MainActivity logging when it is at each step.
---------------------------- PROCESS STARTED (10469) for package com.hoang.daniwebandroidactivityscenario ----------------------------
2022-09-28 17:52:59.082 10469-10469 MAIN_ACTIVITY com...aniwebandroidactivityscenario D On Create
2022-09-28 17:52:59.117 10469-10469 MAIN_ACTIVITY com...aniwebandroidactivityscenario D On Start
2022-09-28 17:52:59.118 10469-10469 MAIN_ACTIVITY com...aniwebandroidactivityscenario D On Resume
2022-09-28 17:52:59.328 10469-10469 MAIN_ACTIVITY com...aniwebandroidactivityscenario D On Start
2022-09-28 17:52:59.328 10469-10469 MAIN_ACTIVITY com...aniwebandroidactivityscenario D On Resume
2022-09-28 17:52:59.393 10469-10469 MAIN_ACTIVITY com...aniwebandroidactivityscenario D On Resume
2022-09-28 17:52:59.506 10469-10469 MAIN_ACTIVITY com...aniwebandroidactivityscenario D On Destroy
---------------------------- PROCESS ENDED (10469) for package com.hoang.daniwebandroidactivityscenario ----------------------------
ActivityScenario vs. ActivityScenarioRule
Android also comes with the ActivityScenarioRule, which is the more preferable method over using ActivityScenario directly. ActivityScenarioRule can automatically handle starting and closing the ActivityScenario for us. It also reduces verbosity because we do not have to call the launch()
method again every time we need it.
To use ActivityScenarioRule, declare it in the class ExampleInstrumentedTest (for Kotlin, Java users can just use the @Rule
annotation).
@get:Rule
val rule = ActivityScenarioRule(MainActivity::class.java)
@Test
fun lifeCycleTestWithRule() {
val scenario = rule.scenario
scenario.moveToState(Lifecycle.State.CREATED)
scenario.moveToState(Lifecycle.State.STARTED)
scenario.moveToState(Lifecycle.State.RESUMED)
scenario.moveToState(Lifecycle.State.RESUMED)
scenario.moveToState(Lifecycle.State.RESUMED)
scenario.moveToState(Lifecycle.State.DESTROYED)
}
To access the ActivityScenario object in the test, either call getScenario()
(Java) or use property accessor syntax in Kotlin (used in example above).
Summary
In this tutorial, we have learned how to use ActivityScenario/ActivityScenarioRule to drive Activity state in our tests. The full project code can be found at https://github.com/dmitrilc/DaniwebAndroidActivityScenario.