androidandroid-uiautomatoruitest

Proper way to go from one Activity to another Activity within the same App using UIAutomator


I am trying to write some UI tests with UIAutomator on Android. I need to use UIAutomator to perform the following actions:

  1. Start the app, wait for the page to be fully loaded.
  2. Click the button to go to another activity. --> This is where I am stuck, I am trying to make it wait until another activity finish rendering (and wait for step 3).
  3. Perform UI tests on the designated activity.

Could anyone please give me an example ? Thanks!


Solution

  • UiDevice & UiObject2 tests classes offer arbitrary timeout wait period associated to the matching condition before proceeding in tests.

    Notice that the timeout value is the maximum amount of time to wait in milliseconds before declaring that the condition is not met; so it doesn't sleep your test until the timeout period expires; instead it tries to match the condition (finding a matched component for instance) and once the condition is met, the test continues without waiting the expiration of the timeout value.

    So, you can assign an arbitrary value that can make sure that the test succeeds. So, it's safe if you want to set it to Long.MAX_VALUE; but make sure that should succeed to avoid ANR. In the below example, I am using 500 milliseconds.

    For instance to launch an app, and wait for it to appear as per documentation:

      var device: UiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
      ....
    
      // Wait for the app to appear
      device.wait(
        Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), // condition
        LAUNCH_TIMEOUT // timeout
        )
      }
    

    Similarly, you can wait/timeout until you can find the target component at the second activity, before proceeding in the test; something like:

    val someView = mDevice.wait(
        Until.findObject(
            By.res( // find object by resource id
                BASIC_SAMPLE_PACKAGE, // application package name
                "myViewId" // id of the view in the second activity
            )
        ),
        500) /* wait 500ms */
    

    So, in your test that you are trying to do, you need to:

    Use the @Before test method (that precedes any test) to make sure the app is launched and its main actvivity is shown:

    private lateinit var mDevice: UiDevice
    private val BASIC_SAMPLE_PACKAGE = "com.example.android......" // change this to your app's package name
    private val LAUNCH_TIMEOUT = 5000L
    
    @Before
    fun startMainActivityFromHomeScreen() {
        // Initialize UiDevice instance
        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
    
        // Start from the home screen
        mDevice.pressHome()
    
        // Wait for launcher
        val launcherPackage = getLauncherPackageName()
        assertThat(launcherPackage, CoreMatchers.notNullValue())
    
        mDevice.wait(
            Until.hasObject(By.pkg(launcherPackage).depth(0)),
            LAUNCH_TIMEOUT
        )
    
        // Launch the blueprint app
        val context = ApplicationProvider.getApplicationContext<Context>()
        val intent = context.packageManager
            .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE)
        intent!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) // Clear out any previous instances
        context.startActivity(intent)
    
        // Wait for the app to appear
    
        mDevice.wait(
            Until.hasObject(
                By.pkg(BASIC_SAMPLE_PACKAGE)
                    .depth(0)
            ),
            LAUNCH_TIMEOUT
        )
    
    
    }
    

    Then to press on a button to launch the second activity:

    // searching for a UI component with a resource Id btn_goto_second
    val secondActivityButton = mDevice.wait(
        Until.findObject(
            By.res(
                BASIC_SAMPLE_PACKAGE,
                "btn_goto_second" // change to your button id
            )
        ),
        500 /* wait 500ms */
    )
    
    // Perform a click on the button to load the second activity.
    secondActivityButton.click()
    

    Then to enter some text to an EditText at the second activity; you can just wait until you find this view; and then type some text:

    // searching for the EditText component with a resource Id edit_text
    val editText = mDevice.wait(
        Until.findObject(
            By.res(
                BASIC_SAMPLE_PACKAGE,
                "edit_text" // change this to yoru editText id
            )
        ),
        500 /* wait 500ms */
    )
    
    // Set the text to the EditText
    editText.text = "some text"
    

    And to verify the text result, use one of the assertion methods:

    assertThat(
        editText.text,
        CoreMatchers.`is`(equalTo("some text"))
    )
    

    You can check the documentation for further help; or refer to their sample app. Also this is a nice repo that would help.