1

I am developing an Android app using Kotlin. I am adding instrumented tests in my project. Now, I am struggling with testing if an activity is not opened after launching another activity. This is my scenario.

I have MainActivity that starts LoginActivity.

This is the code the MainActivity

class MainActivity : AppCompatActivity() {

    companion object {
        val LAUNCH_DELAY: Long = 2000
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Handler().postDelayed({
            this.startLoginActivity()
            finish() //please, pay attention here
        }, LAUNCH_DELAY)
    }

    protected fun startLoginActivity()
    {
        startActivity(Intent(this, LoginActivity::class.java))
    }
}

As you can see in the code above, I am calling finish() method to kill the activity after launching LoginActivity. I want to test if the back button is pressed within login activity which is started by the main activity, the application is closed and it does not go back to the main activity.

This is my test class

@RunWith(AndroidJUnit4::class)
class MainActivityTest {
    @Rule @JvmField
    val mainActivityRule: ActivityTestRule<MainActivity> = ActivityTestRule<MainActivity>(MainActivity::class.java)
    @Rule @JvmField
    val loginActivityRule: ActivityTestRule<LoginActivity> = ActivityTestRule<LoginActivity>(LoginActivity::class.java)

    @Before
    fun setUp() {
    }

    @Test
    fun mainActivityHistoryIsForgottenAfterStartingLoginActivity()
    {
        Intents.init()
        Thread.sleep(MainActivity.LAUNCH_DELAY);
        Intents.release()

        pressBack()
        //here how can I assert if the main activity is not open.
    }
}

Please, pay attention to the comment in the code. I like to assert if the main activity is not opened or it does not go back to the main activity when the back button is pressed int the login activity. How can I test it?

Wai Yan Hein
  • 13,651
  • 35
  • 180
  • 372

1 Answers1

1

Since you want to test the high level interactions with your app, you should use the UI Automator test framework.

First add the library to build.gradle:

dependencies {
    ...
    androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
}

Then write a test like this:

@RunWith(AndroidJUnit4::class)
class MainActivityTest {
    @get:Rule
    val mainActivityRule = ActivityTestRule(MainActivity::class.java)

    @Test
    fun mainActivityHistoryIsForgottenAfterStartingLoginActivity()
    {
        // mainActivityRule makes sure MainActivity is launched, so we just need to wait
        // for MainActivity to launch LoginActivity
        Thread.sleep(MainActivity.LAUNCH_DELAY)

        // Wait some more since the system is not instantaneous
        Thread.sleep(1000)

        // Now LoginActivity should be showing. Press the back button
        val uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
        uiDevice.pressBack()

        // Wait some more still since the system is not instantaneous
        Thread.sleep(1000)

        // Make sure that we have left our app, i.e. pressing back have closed our app
        // BuildConfig.APPLICATION_ID is the package name of our app
        // So if the currently active package (app) is NOT our own app, it means
        // our own app was closed, which is what we want to test.
        assertNotEquals(uiDevice.currentPackageName, BuildConfig.APPLICATION_ID)
    }
}

Note that you should not have this:

@Rule @JvmField
val loginActivityRule: ActivityTestRule<LoginActivity> = ActivityTestRule<LoginActivity>(LoginActivity::class.java)

since that will launch LoginActivity at the beginning of the test. You don't want that. You want MainActivity to launch LoginActivity. Also note that you can simplify

@Rule @JvmField
val mainActivityRule: ActivityTestRule<MainActivity> = ActivityTestRule<MainActivity>(MainActivity::class.java)

to

@get:Rule
val mainActivityRule = ActivityTestRule(MainActivity::class.java)

And you don't need any

@Before
fun setUp() {
}

since it is empty and does nothing.

Enselic
  • 4,434
  • 2
  • 30
  • 42