Android – espresso – clicking on a listview entry based on custom objects

Espresso is used for automatic testing my App.

Edit: below you find a number of answers!

  • Google sign in and sign out in android using g-plus latest API
  • Cancel Done buttons in Calendar App - Is it part of Action Bar?
  • Android Instant Run Slow Builds
  • New PhoneGap 3.0 The import org.apache.cordova cannot be resolved
  • How to support MOV(quick time) in android?
  • Loading remote images
  • How can I click (within an automated Espresso test script) on an entry in a long list of custom objects?

    In the Espresso documentation there is an example of a LongList. Working with a List of objects is what I normally do. Trying many options to step from Map to Object didn’t yield good results so far.

    The Espresso documentation says a ‘onData’ should be used. So, something like:

    onData( myObjectHasContent("my_item: 50")).perform(click());
    onView(withId( R.id.selection_pos2)).check(matches(withText("50")));
    

    My questions (and I think they are helpful for the learning community):
    – Can you write a good Matcher for this?
    – How can we use that in the ‘onData’ ?

    What is the situation? On the screen I have a listview of objects like:

    public class MyOjbect { 
        public String content; 
        public int    size; 
    }
    

    The adapter I use to populate the populated list is:

    public class MyObjectWithItemAndSizeAdapter extends ArrayAdapter<MyObjectWithItemAndSize> {
        private final Context context;
        private final List<MyObjectWithItemAndSize> values;
        ...
        @Override
        public View getView(int position, View concertView, ViewGroup parent) {
            View view = null;
            if (concertView != null) {
                view = (LinearLayout) concertView;
            } else {
                LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate( R.layout.list_item, parent, false);
            } 
            TextView itemT = (TextView) view.findViewById( R.id.item_content);
            itemT.setText( values.get(position).item);
            TextView sizeT = (TextView) view.findViewById( R.id.item_size);
            sizeT.setText( "" + values.get(position).size);
            return view;
        }
     }
    

    Related posts:

    How to change spinner text size and text color?
    Android Device Monitor doesn&#039;t open - error on log file
    ActionBar custom title as ImaveView instead of TextView
    When should I do certain SQLite operations on another thread(not the main thread)?
    Data encryption on Android, AES-GCM or plain AES?
    Paho MQTT Android Service Issue
  • Why is glClear blocking in OpenGLES?
  • Change the step size of a NumberPicker
  • Display classes containing deprecation Android Studio
  • How to correctly handle action mode “done” button?
  • Google App Developer: Change Default Language of App
  • How to compile a C program for Genymotion (Android x86)
  • 3 Solutions collect form web for “Android – espresso – clicking on a listview entry based on custom objects”

    The matcher given to onData() must match the desired value returned by Adapter.getItem(int) of the desired ListView.

    So in your example, the matcher should be something like this:

    public static Matcher<Object> withContent(final String content) {
        return new BoundedMatcher<Object, MyObjectWithItemAndSize>(MyObjectWithItemAndSize.class) {
            @Override
            public boolean matchesSafely(MyObjectWithItemAndSize myObj) {
                return myObj.content.equals(content);
            }
    
            @Override
            public void describeTo(Description description) {
                description.appendText("with content '" + content + "'");
            }
        };
    }
    

    An addition to the previous answer, I create a complete version with 2 kind of validations. This may help you understand Espresso and Custom Matchers.

    The difference with the standard Espresso LongList example is that I use a list of Custom objects to show in a listview. Scrolling to the right list-entry and checking the result is shown below.

    Method 1 – the validation against a string

    In the test script is:

    onData( allOf( instanceOf( MyObjectWithItemAndSize.class), myCustomObjectShouldHaveString( "my_item: 60")))
             .perform(click());
    // testing the result ... as in the longlist example
    onView(withId(R.id.selection_pos2)).check(matches(withText("my_item: 60"))); 
    

    The matcher is:

    public static Matcher<Object> myCustomObjectShouldHaveString( String expectedTest) {
        return myCustomObjectShouldHaveString( equalTo( expectedTest));
    }
    private static Matcher<Object> myCustomObjectShouldHaveString(final Matcher<String> expectedObject) {
    return new BoundedMatcher<Object, MyObjectWithItemAndSize>( MyObjectWithItemAndSize.class) {
        @Override
        public boolean matchesSafely(final MyObjectWithItemAndSize actualObject) {
            // next line is important ... requiring a String having an "equals" method
            if( expectedObject.matches( actualObject.item) ) {
                 return true;
               } else { 
                 return false;
               }
          }
          @Override
          public void describeTo(final Description description) {
             // could be improved, of course
             description.appendText("getnumber should return ");
          }
       };
    }
    

    Method 2: validating against the (complete object).

    In the test script you find:

    MyObjectWithItemAndSize myObject = new MyObjectWithItemAndSize( "my_item: 60", 11);
    onData( allOf( instanceOf( MyObjectWithItemAndSize.class), myObjectHasContent( myObject))).perform( click());
    onView(withId( R.id.selection_pos2)).check(matches(withText("my_item: 60"))); 
    

    The matcher is.

    The most important line (I have been struggling with) is below // ****

    public static Matcher<Object> myObjectHasContent( MyObjectWithItemAndSize expectedObject) {
       return myObjectHasContent( equalTo( expectedObject));
    }
    //private method that does the work of matching
    private static Matcher<Object> myObjectHasContent(final Matcher<MyObjectWithItemAndSize> expectedObject) {
         return new BoundedMatcher<Object, MyObjectWithItemAndSize>(MyObjectWithItemAndSize.class) {
            @Override
            public boolean matchesSafely( final MyObjectWithItemAndSize actualObject) {
                // ****** ... the 'matches'. See below. 
                // this requires the MyObjectWithItemAndSize to have an 'equals' method
                if( expectedObject.matches( actualObject) ) {
                    return true;
                } else { 
                    return false;
                }
            }
            @Override
            public void describeTo(final Description description) {
               description.appendText("getnumber should return ");
            }
         };
      }
    

    What is very important is that the Custom object has this method (and I guess overriden):

    @Override
    public boolean equals( Object mob2) {
        return( (this.item.equals( ((MyObjectWithItemAndSize) mob2).item)));
        // of course, could have also a check on this.size.
    } 
    

    And it works!!!! Pfff, took a while, but overcame. Thanks also to Yash F.

    I needed to test and AdapterView with a custom adapter using Espresso 2 today. I ended up using FeatureMatcher:

    private static FeatureMatcher<Product, String> withProductName(final String productName) {
        return new FeatureMatcher<Product, String>(equalTo(productName), "with productName", "productName") {
            @Override
            protected String featureValueOf(Product actual) {
                return actual.name;
            }
        };
    }
    

    And then calling this utility method from a test like follows:

    onData(withProductName("My Awesome Product"))
                .inAdapterView(withId(R.id.product_list))
                .onChildView(withId(R.id.product_title))
                .check(matches(withText("My Awesome Product")));
    

    I think FeatureMatcher is great when you want to assert a specific property of a data object.

    Android Babe is a Google Android Fan, All about Android Phones, Android Wear, Android Dev and Android Games Apps and so on.