Testing ViewPager with Espresso. How perfom action to a button of an Item?

23,185

FirstVal, ViewPager is not an AdapterView, it directly extends from ViewGroup. So the method onData() cannot be used on a ViewPager.

Solution 1

As it's a ViewGroup, each items are direct children of its ViewPager. So the process is to reference the first view child using a custom matcher (like this onefirstChildOf()) and playing with hasDescendant() and isDescendantOfA() to access the targeted view and perform an action on it.

 onView(allOf(withId(R.id.button), isDescendantOfA(firstChildOf(withId(R.id.viewpager)))))
            .perform(click());

Solution 2 (da best)

As the particularity of the ViewPager is to display each items (#1 solution's child views) that composed it, one by one (page-by-page style). So even if your items use the same layout with the same IDs, only one is displayed. So we can reference the targeted view by its Id and add the constraints isDisplayed(). It will match only one view, the one that is currently displayed.

onView(allOf(withId(R.id.button), isDisplayed())).perform(click());

as simple as that.

And if you want another item, you may perform a swipe() on your ViewPager to change the displayed item:

onView(withId(R.id.viewpager)).perform(swipeLeft());

Notes from the Android dev doc:

isDisplayed will select views that are partially displayed (eg: the full height/width of the view is greater than the height/width of the visible rectangle). If you wish to ensure the entire rectangle this view draws is displayed to the user use isCompletelyDisplayed()

Thanks @TWiStErRob

Share:
23,185
damson
Author by

damson

Updated on February 04, 2021

Comments

  • damson
    damson over 3 years

    I have a ViewPager whith items containing only a picture and a button.

    I can't successfully interact with the UI of an item (Page) because, except the displayed picture, there is nothing to differentiate (from UI point of view) all items of the ViewPager.

    I tried to select only one item with a position:

    onData(is(instanceOf(ItemClass.class)))
                .atPosition(0)
                .onChildView(withId(R.id.button))
                .perform(click());
    

    causing:

    NoMatchingViewException: No views in hierarchy found matching: is assignable from class: class android.widget.AdapterView

    How to access and test items of a ViewPager with Espresso ?

  • TWiStErRob
    TWiStErRob over 7 years
    You might want to use isCompletelyDisplayed(). In my experience isDisplayed() also matches views in off-screen pages.
  • Fabian valencia
    Fabian valencia over 7 years
    @TWiStErRob has given an awesome tip, check this answer stackoverflow.com/a/33516360/6373011
  • zzz
    zzz over 5 years
    can you add position in this, as all the list is having same ids...onView(allOf(withId(R.id.button), isDisplayed())).perform(click());
  • Henrik Gyllensvärd
    Henrik Gyllensvärd over 5 years
    I had similar issues with a ViewPager which used the same view but with different items. Where as @TWiStErRob mentioned isDisplayed() matched off-screen views. So I tried isCompletelyDisplayed() but as I was swiping just before the check, even with animations turned off, Espresso doesn't seem to manage that. So what I ended up doing was using: onView(allOf(withId(R.id.recyclerView), isDisplayingAtLeast(60))) And the recycler view is now correctly identified all the time.