How to use Espresso to test item in adapter at a s

2019-06-21 18:22发布

问题:

I'm trying to use Espresso (2.0) to verify that the text in in a list adapter item at a given position is correct, and for the life of my I can't figure out the correct methods to call.

My Adapter type (IconRowAdapter) contains a list of IconRow objects. Each IconRow has a getText() method that returns the text of that item.

Here's the non-Espresso working test code that verifies that the IconRow object at position 0 in the adapter has the expected text ("Artists").

public void testHomeActivityMenu() {
    ListView list = (ListView) getActivity().findViewById(R.id.item_list);
    IconRowAdapter adapter = (IconRowAdapter) list.getAdapter();

    assertEquals(adapter.getItem(0).getText(), "Artists");
}

This works.

I've tried assorted variations on the following Espresso code to do the same thing,

onData(is(instanceOf(IconRowAdapter.class)))
        .atPosition(0)
        .check(matches(withItemContent("Artists")));

where withItemContent() looks like this:

public static Matcher<Object> withItemContent(String expectedText) {
    checkNotNull(expectedText);
    return withItemContent(equalTo(expectedText));
}

@SuppressWarnings("rawtypes")
public static Matcher<Object> withItemContent(final Matcher<String> itemTextMatcher) {
    checkNotNull(itemTextMatcher);
    return new BoundedMatcher<Object, IconRow>(IconRow.class) {
        @Override
        public boolean matchesSafely(IconRow iconRow) {
            return itemTextMatcher.matches(iconRow.getText());
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("with item content: ");
            itemTextMatcher.describeTo(description);
        }
    };
}

What I would expect that to do is:

  • Get the data from the adapter that's an instance of IconRowAdapter (of which there's only one in the activity)...
  • ... find the entry at position 0 in the adapter...
  • ... use withItemContent() to check that the text in the item at that position matches "Artists"

When I run that I get the following error:

Caused by: java.lang.RuntimeException: No data found matching: is an instance of 
uk.org.ngo.squeezer.IconRowAdapter contained values: <[Data: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow@b1585980 (class: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow) token: 0, Data: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow@b15859e0 (class: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow) token: 1, Data: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow@b1585a00 (class: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow) token: 2, Data: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow@b1585a20 (class: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow) token: 3, Data: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow@b1585a40 (class: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow) token: 4, Data: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow@b1585a60 (class: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow) token: 5, Data: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow@b1585a80 (class: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow) token: 6, Data: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow@b1585aa0 (class:  
uk.org.ngo.squeezer.IconRowAdapter$IconRow) token: 7, Data: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow@b1585ac0 (class: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow) token: 8, Data: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow@b1585ae0 (class: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow) token: 9, Data: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow@b1585b00 (class: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow) token: 10, Data: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow@b1585b20 (class: 
uk.org.ngo.squeezer.IconRowAdapter$IconRow) token: 11]>

There are 12 items in IconRowAdapter, so I have some confidence that it's looking in the correct adapter.

All the example code and documentation that I've been able to find assumes that you're trying to find an entry in the adapter in order to click on it (and that this will result in the value in some other view changing). I can't find anything that talks about how how to check the value of a given item in an adapter.

Any insights gratefully received.

Edit to add:

What does work is the following:

onData(anything())
        .inAdapterView(withId(R.id.item_list))
        .atPosition(0)
        .check(matches(hasDescendant(
                allOf(withId(R.id.text1), withText(containsString("Artists"))))));

If I understand that properly that's testing the value of the R.id.text1 view, rather than the value in the adapter. I guess that makes sense for a UI test, but I'm still interested in finding out how (if?) I can use Espresso to test the contents of an item in the adapter.

回答1:

The matcher passed as argument to onData() must match the value as returned by Adapter.getItem(). So the first version doesn't match, because of the wrong type being used. It should be:

onData(is(instanceOf(IconRowAdapter.IconRow.class)))

What also can be a pitfall is using equalTo on different kinds of CharSequences. String is a CharSequence, but if IconRow.getText() returns CharSequence instead of String, then this can also be Spannable, Editable, etc in which case equalTo wouldn't match. So if IconRow.getText() return anything but String, make sure to convert it into a String before comparison.



回答2:

Try this out it must work:

//check that item is present in list at position 0
onData(withItemContent("Artists"))
    .inAdapterView(withId(R.id.list_view))
    .atPosition(0)
    .check(matches(isDisplayed()));

If you delete atPosition(0), then you'll just check presence in the adapter view.