Espresso is used for automatic testing my App.
Edit: below you find a number of answers!
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;
}
}
I needed to test an 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With