It looks like CoordinatorLayout
breaks the behaviour of Espresso actions such as scrollTo()
or RecyclerViewActions.scrollToPosition()
.
Issue with NestedScrollView
For a layout like this one:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
...
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
...
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
If I try to scroll to any view inside the NestedScrollView
using ViewActions.scrollTo()
the first problem I find is that I get a PerformException
. This is because this action only supports ScrollView
and NestedScrollView
doesn't extend it. A workaround for this problem is explained here, basically we can copy the code in scrollTo()
and change the constrains to support NestedScrollView
. This seems to work if the NestedScrollView
is not in a CoordinatorLayout
but as soon as you put it inside a the CoordinatorLayout
the scrolling action fails.
Issue with RecyclerView
For the same layout, if I replace the NestedScrollView
with a RecyclerView
there is also problems with the the scrolling.
In this case I'm using RecyclerViewAction.scrollToPosition(position)
. Unlike the NestedScrollView
, here I can see some scrolling happening. However, it looks like it scrolls to the wrong position. For example, if I scroll to the last position, it makes visible the second to last but not the last one. When I move the RecyclerView
out of the CoordinatorLayout
the scrolling works as it should.
At the moment we can't write any Espresso test for the screens that use CoordinatorLayout
due to this issues. Anyone experiencing the same problems or knows a workaround?
Here is how I did the same thing that @miszmaniac did in Kotlin. With delegation in Kotlin, it is much cleaner and easier because I don't have to override the methods I don't need to.
The solution of Mr Mido may work in some situations, but not always. If you have some view in the bottom of screen, the scroll of your RecyclerView will not happen because the click will start outside the RecyclerView.
One way to workaround this problem is to write a custom SwipeAction. Like this:
1 - Create the CenterSwipeAction
2 - Create the method to return the ViewAction
3 - Then use it to scroll the screen:
And that's it! This way you can control how the scroll is going to happen in your screen.
I've made a NestedScrollViewScrollToAction class.
I think it's better place to make activity specific stuff there instead.
The only thing worth mentioning is that code searches for parent nestedScrollView and removes it's CoordinatorLayout behaviour.
https://gist.github.com/miszmaniac/12f720b7e898ece55d2464fe645e1f36
This is happening because the Espresso scrollTo() method explicitly checks the layout class and only works for ScrollView & HorizontalScrollView. Internally it's using View.requestRectangleOnScreen(...) so I'd expect it to actually work fine for many layouts.
My workaround for NestedScrollView was to take ScrollToAction and modify that constraint. The modified action worked fine for NestedScrollView with that change.
Changed method in ScrollToAction class:
Convenience method:
Barista's
scrollTo(R.id.button)
works on all kinds of scrollable views, also onNestedScrollView
.It's useful to fix this kind of issues with Espresso. We develop and use it just to write Espresso tests in a fast and reliable way. And here's a link: https://github.com/SchibstedSpain/Barista
I had this issue with CoordinatorLayout->ViewPager->NestedScrollView an easy work around from me to get the same scrollTo() behavior was to just swipe up on the screen: