androidandroid-uiautomatorexpresso

Android Expresso/UI Automator How do i automate clicking on Android screens outside my app (app picker)


My Android application has a button to download a file and then send it to an application on the device. Android pops up a screen listing the Applications on the device for the user to select which application to use.

I would like to automate this flow but i can not see how I can automate clicking on the Application Picker that Android presents. I presume this is because it is outside of my application.

I tried using Android Studio's "Record Expresso Test", I performed the following test steps

  1. click on the action which sends my image to an app on the device (action1)
  2. saw the Android Application picker appear and chose photos
  3. Clicked back to close photos app and go back to my app
  4. click on a different action in my app (action2)

I see in the recorded test code for steps 1 and 4 above, but nothing for 2 and 3. Therefore it makes me think that Expresso can not be used for this particular test flow.

Does anyone know how I could test this flow using Expresso?

EDIT:

Thank you to "John O'Reilly" for recommending UI Automator. I can see that I can use the UI Automator code within my Expresso test successfully. However I am having trouble writing a precise verification of the Application Selector.

The selector will have a title of "Open With". Using Android Device Monitor I can see the hierarchy of objects as illustrated below.

enter image description here

Some classes and IDs are internal so I can not search on those things. I do not want to code to look for a specific application as when the test is run on another machine it may not have that application. I just need to verify that the application picker has been displayed.

// the app selector has a FrameLayout as one of its parent views, and a child Text View which has the "Open With" title
UiObject labelOnly = new UiObject(new UiSelector()
        .className("android.widget.FrameLayout")
        .childSelector(new UiSelector()
                .className("android.widget.TextView")
                .text(openWithLabel)
        )
);
boolean labelOnly_exists = labelOnly.exists();

// the app selector has a FrameLayout as one of its parent views, and a child ListView (containing the apps)
UiObject listOnly = new UiObject(new UiSelector()
        .className("android.widget.FrameLayout")
        .childSelector(new UiSelector()
                .className("android.widget.ListView")
        )
);
boolean listOnly_exists = listOnly.exists();  

// I can use the listView to search for a specific app, but this makes the tests fragile if a different device does not have that app installed
UiObject listAndAppName = new UiObject(new UiSelector()
        .className("android.widget.ListView")
        .instance(0)
        .childSelector(new UiSelector()
                .text("Photos")));
boolean listAndAppName_exists = listAndAppName.exists();

How could i write a statement that verifies that what is on the screen is the application picker? I was hoping maybe have a selector that searches for a FrameLayout which has a child textView containing "Open With" and also contains a child ListView. With these 2 checks together it should identify only the application picker.


Solution

  • The credit for the answer to this question should go to John O'Reilly who pointed me to use the UI Automator.

    I resolved the issue of checking what Android screen is invoked when my test clicks on an action by just checking there is a TextView on the screen with the title I am expecting. Its not perfect as this will pass if there is any TextView on the screen with the text so does not precisely check its the application picker.

    However, for my test this should be enough of a check as my app (which would be behind the app picker) should not have a TextView with the expected title, so if the title is found its pretty much likely to be the application picker.

    public static boolean verifyAndroidScreenTitlePresent(String title) {
        UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    
        UiObject titleTextUI = new UiObject(new UiSelector()
                .className("android.widget.TextView")
                .text(title)
        );
        boolean titleExists = titleTextUI.exists();
    
        // close the app selector to go back to our app so we can carry on with Expresso
        mDevice.pressBack();
    
        return titleExists;
    }