Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check if a View is VISIBLE on a specific RecyclerView item?

I am new to using espresso. I cannot really get what I wanted to test using this code:

    onData(withId(R.id.relativelayout_tag))
        .inAdapterView(withId(R.id.recyclerview_tag_list))
        .onChildView(withId(R.id.imageview_tag))
        .atPosition(1)
        .check(matches(isDisplayed()));

Where R.id.imageview_tag is a child of R.id.relativelayout_tag. R.id.relativelayout_tag holds the entire content for my adapter item. R.id.recyclerview_tag_list is the name of my RecyclerView on which I assigned a particular RecyclerView Adapter.

This is a very very basic test. Here are the user procedures:

  1. Select, SPECIFICALLY, the first item on the RecyclerView (I don't really care what text is on the view). Also do not suggest using view text to identify the first item. I DONT CARE ABOUT THE ADAPTER ITEM'S CONTENT or even putting a unique tag on some view.
  2. On select, the indicator view(An ImageView that shows that the item is selected) should appear.

Very basic and simple. It is so hard to write a test for this basic user story using Espresso. When I run that particular test it always fail stating this:

Caused by: java.lang.RuntimeException: Action will not be performed because the target view does not match one or more of the following constraints:
(is assignable from class: class android.widget.AdapterView and is displayed on the screen to the user)
Target view: "RecyclerView{id=2131624115, res-name=recyclerview_tag_list, visibility=VISIBLE, width=480, height=1032, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=15}"

It doesn't make sense as I have the list already visible. I can even run this test just fine:

    onView(withId(R.id.recyclerview_tag_list))
            .perform(RecyclerViewActions
            .actionOnItemAtPosition(1, click()));

Here is the full test:

@Test
public void shouldTagToggleSelected()
{
    onView(withId(R.id.recyclerview_tag_list))
            .perform(RecyclerViewActions
            .actionOnItemAtPosition(1, click()));

    onData(withId(R.id.relativelayout_tag))
        .inAdapterView(withId(R.id.recyclerview_tag_list))
        .onChildView(withId(R.id.imageview_tag))
        .atPosition(1)
        .check(matches(isDisplayed()));

    //onView(withId(R.id.imageview_tag))
    //        .check(matches(isDisplayed()));
}

What I wanted to test if the indicator view has a visibility set to visible ONLY on that particular item (or any item of my choosing).

Any thoughts? Maybe I missed something dearly.

Thanks a lot!

like image 807
Neon Warge Avatar asked Sep 25 '16 14:09

Neon Warge


People also ask

How can I check if a RecyclerView item is visible?

OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super. onScrolled(recyclerView, dx, dy); LinearLayout ll = (LinearLayout) recyclerView. findChildViewUnder(dx, dy); if (ll != null) { TextureVideoView tvv = (TextureVideoView) ll.

What is Item view in RecyclerView?

A ViewHolder describes an item view and metadata about its place within the RecyclerView. RecyclerView. Adapter implementations should subclass ViewHolder and add fields for caching potentially expensive View. findViewById(int) results.


2 Answers

onData does not work with RecyclerView as RecyclerView does not extend AdapterView.

You need to use onView to make your assertions. If it's the first item in the recycler view, then you can use something like this matcher to make your assertion:

public static Matcher<View> withViewAtPosition(final int position, final Matcher<View> itemMatcher) {
        return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) {
            @Override
            public void describeTo(Description description) {
                itemMatcher.describeTo(description);
            }

            @Override
            protected boolean matchesSafely(RecyclerView recyclerView) {
                final RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(position);
                return viewHolder != null && itemMatcher.matches(viewHolder.itemView);
            }
        };
}

And the usage is the following:

    onView(withId(R.id.recyclerview_tag_list))
            .check(matches(withViewAtPosition(1, hasDescendant(allOf(withId(R.id.imageview_tag), isDisplayed())))));

Keep in mind that this matcher will fail if your ViewHolder has not been laid out yet. If that's the case you need to scroll to the ViewHolder with RecyclerViewActions. You don't need to scroll if you are clicking the item in the test prior to using the matcher.

like image 105
Be_Negative Avatar answered Oct 22 '22 22:10

Be_Negative


I had an exception error when checking element linear layout that was not visible. To resolve it, I had to add withFailureHandler as follows:

class MyTest {

        boolean isViewVisible = true;

        public void test1(){

           onView(withRecyclerView(R.id.recycler_view)
                .atPositionOnView(index, R.id.linear_layout))
                .withFailureHandler(new FailureHandler() {
                    @Override
                    public void handle(Throwable error, Matcher<View> viewMatcher){

                        isViewVisible = false;                            
                    }
                })
          .check(isVisible());

          //wait for failure handler to finish
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }

          if(isViewVisible){

              //do something
          }
        }
}

class MyTestUtil {

    public static RecyclerViewMatcher withRecyclerView(final int recyclerViewId) {

        return new RecyclerViewMatcher(recyclerViewId);
    }

    public static ViewAssertion isVisible() {

        return new ViewAssertion() {
            public void check(View view, NoMatchingViewException noView) {
                assertThat(view, new VisibilityMatcher(View.VISIBLE));
            }
        };
    }
}


public class RecyclerViewMatcher {
    private final int recyclerViewId;

    public RecyclerViewMatcher(int recyclerViewId) {
        this.recyclerViewId = recyclerViewId;
    }

    public Matcher<View> atPosition(final int position) {
        return atPositionOnView(position, -1);
    }

    public Matcher<View> atPositionOnView(final int position, final int targetViewId) {

        return new TypeSafeMatcher<View>() {
            Resources resources = null;
            View childView;

            public void describeTo(Description description) {
                String idDescription = Integer.toString(recyclerViewId);
                if (this.resources != null) {
                    try {
                        idDescription = this.resources.getResourceName(recyclerViewId);
                    } catch (Resources.NotFoundException var4) {
                        idDescription = String.format("%s (resource name not found)",
                                new Object[] { Integer.valueOf
                                        (recyclerViewId) });
                    }
                }

                description.appendText("with id: " + idDescription);
            }

            public boolean matchesSafely(View view) {

                this.resources = view.getResources();

                if (childView == null) {
                    RecyclerView recyclerView =
                            (RecyclerView) view.getRootView().findViewById(recyclerViewId);
                    if (recyclerView != null && recyclerView.getId() == recyclerViewId) {
                        childView = recyclerView.findViewHolderForAdapterPosition(position).itemView;
                    }
                    else {
                        return false;
                    }
                }

                if (targetViewId == -1) {
                    return view == childView;
                } else {
                    View targetView = childView.findViewById(targetViewId);
                    return view == targetView;
                }

            }
        };
    }
}
like image 37
Hau Le Avatar answered Oct 23 '22 00:10

Hau Le