71

Using Espresso and Hamcrest,

How can I count items number available in a recyclerView?

Exemple: I would like check if 5 items are displaying in a specific RecyclerView (scrolling if necessary).

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Boris S.
  • 1,068
  • 1
  • 8
  • 18
  • An answer Related to this [Answer](https://stackoverflow.com/questions/43959582/how-to-check-if-a-value-exists-in-firebase-database-android/62484330#62484330) – Sarvesh Hon Jun 20 '20 at 10:26

8 Answers8

114

Here an example ViewAssertion to check RecyclerView item count

public class RecyclerViewItemCountAssertion implements ViewAssertion {
  private final int expectedCount;

  public RecyclerViewItemCountAssertion(int expectedCount) {
    this.expectedCount = expectedCount;
  }

  @Override
  public void check(View view, NoMatchingViewException noViewFoundException) {
    if (noViewFoundException != null) {
        throw noViewFoundException;
    }

    RecyclerView recyclerView = (RecyclerView) view;
    RecyclerView.Adapter adapter = recyclerView.getAdapter();
    assertThat(adapter.getItemCount(), is(expectedCount));
  }
}

and then use this assertion

onView(withId(R.id.recyclerView)).check(new RecyclerViewItemCountAssertion(5));

I have started to write an library which should make testing more simple with espresso and uiautomator. This includes tooling for RecyclerView action and assertions. https://github.com/nenick/espresso-macchiato See for example EspRecyclerView with the method assertItemCountIs(int)

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
nenick
  • 7,340
  • 3
  • 31
  • 23
36

Adding a bit of syntax sugar to the @Stephane's answer.

public class RecyclerViewItemCountAssertion implements ViewAssertion {
    private final Matcher<Integer> matcher;

    public static RecyclerViewItemCountAssertion withItemCount(int expectedCount) {
        return withItemCount(is(expectedCount));
    }

    public static RecyclerViewItemCountAssertion withItemCount(Matcher<Integer> matcher) {
        return new RecyclerViewItemCountAssertion(matcher);
    }

    private RecyclerViewItemCountAssertion(Matcher<Integer> matcher) {
        this.matcher = matcher;
    }

    @Override
    public void check(View view, NoMatchingViewException noViewFoundException) {
        if (noViewFoundException != null) {
            throw noViewFoundException;
        }

        RecyclerView recyclerView = (RecyclerView) view;
        RecyclerView.Adapter adapter = recyclerView.getAdapter();
        assertThat(adapter.getItemCount(), matcher);
    }
}

Usage:

    import static your.package.RecyclerViewItemCountAssertion.withItemCount;

    onView(withId(R.id.recyclerView)).check(withItemCount(5));
    onView(withId(R.id.recyclerView)).check(withItemCount(greaterThan(5)));
    onView(withId(R.id.recyclerView)).check(withItemCount(lessThan(5)));
    // ...
JaydeepW
  • 3,237
  • 1
  • 25
  • 27
Brais Gabin
  • 5,827
  • 6
  • 57
  • 92
  • Wonderful. I would suggest getting this into espresso because it is really strange that such a core feature is not available. – Samuel Feb 18 '18 at 11:30
  • Does not work no more. A white screen is shown and no response more is collected, eventually failing the test. – Karunesh Palekar Mar 01 '22 at 04:46
21

To complete nenick answer and provide and little bit more flexible solution to also test if item cout is greaterThan, lessThan ...

public class RecyclerViewItemCountAssertion implements ViewAssertion {

    private final Matcher<Integer> matcher;

    public RecyclerViewItemCountAssertion(int expectedCount) {
        this.matcher = is(expectedCount);
    }

    public RecyclerViewItemCountAssertion(Matcher<Integer> matcher) {
        this.matcher = matcher;
    }

    @Override
    public void check(View view, NoMatchingViewException noViewFoundException) {
        if (noViewFoundException != null) {
            throw noViewFoundException;
        }

        RecyclerView recyclerView = (RecyclerView) view;
        RecyclerView.Adapter adapter = recyclerView.getAdapter();
        assertThat(adapter.getItemCount(), matcher);
    }

}

Usage:

onView(withId(R.id.recyclerView)).check(new RecyclerViewItemCountAssertion(5));
onView(withId(R.id.recyclerView)).check(new RecyclerViewItemCountAssertion(greaterThan(5));
onView(withId(R.id.recyclerView)).check(new RecyclerViewItemCountAssertion(lessThan(5));
// ...
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Stéphane
  • 1,528
  • 14
  • 21
18

Validated answer works but we can solve this problem with one line and without adapter awareness :

onView(withId(R.id.your_recycler_view_id)).check(matches(hasChildCount(2)))

Replace your_recycler_view_id with your id and 2 with the number to assert.

DamienL
  • 577
  • 1
  • 5
  • 14
  • IDE says: Cannot resolve method `hasChildCount(int)` – A. K. M. Tariqul Islam Jul 25 '19 at 14:48
  • 1
    Don't forget to import `androidx.test.espresso.matcher.ViewMatchers.hasChildCount` – DamienL Jul 26 '19 at 15:19
  • 2
    Does this count the total number of items that can be displayed (i.e. the adapter's `getItemCount()`) or does it count the number of recycled views currently in the RecyclerView? – Michael Osofsky Sep 17 '19 at 18:27
  • 2
    Good question. Probably not the adapter's `getItemCount()`. So this solution is only feasible when you can have limited number of items and not on all devices. – DamienL Sep 20 '19 at 10:10
12

Based on @Sivakumar Kamichetty answer:

  1. Variable 'COUNT' is accessed from within inner class, needs to be declared final.
  2. Unnecessarily line: COUNT = 0;
  3. Transfer COUNT variable to one element array.
  4. Variable result is unnecessary.

Not nice, but works:

public static int getCountFromRecyclerView(@IdRes int RecyclerViewId) {
    final int[] COUNT = {0};
    Matcher matcher = new TypeSafeMatcher<View>() {
        @Override
        protected boolean matchesSafely(View item) {
            COUNT[0] = ((RecyclerView) item).getAdapter().getItemCount();
            return true;
        }
        @Override
        public void describeTo(Description description) {}
    };
    onView(allOf(withId(RecyclerViewId),isDisplayed())).check(matches(matcher));
    return COUNT[0];
}
t0m
  • 3,004
  • 31
  • 53
10

You can create a custom BoundedMatcher:

object RecyclerViewMatchers {
    @JvmStatic
    fun hasItemCount(itemCount: Int): Matcher<View> {
        return object : BoundedMatcher<View, RecyclerView>(
            RecyclerView::class.java) {

            override fun describeTo(description: Description) {
                description.appendText("has $itemCount items")
            }

            override fun matchesSafely(view: RecyclerView): Boolean {
                return view.adapter.itemCount == itemCount
            }
        }
    }
}

And then use it like this:

onView(withId(R.id.recycler_view)).check(matches((hasItemCount(5))))
makovkastar
  • 5,000
  • 2
  • 30
  • 50
9

I used the below method to get the count of RecyclerView

public static int getCountFromRecyclerView(@IdRes int RecyclerViewId) {
int COUNT = 0;
        Matcher matcher = new TypeSafeMatcher<View>() {
            @Override
            protected boolean matchesSafely(View item) {
                COUNT = ((RecyclerView) item).getAdapter().getItemCount();
                return true;
            }
            @Override
            public void describeTo(Description description) {
            }
        };
        onView(allOf(withId(RecyclerViewId),isDisplayed())).check(matches(matcher));
        int result = COUNT;
            COUNT = 0;
        return result;
    }

Usage -

int itemsCount = getCountFromRecyclerView(R.id.RecyclerViewId);

Then perform assertions to check if the itemsCount is as expected

-2

count with ActivityScenarioRule

@get: Rule
val activityScenarioRule = ActivityScenarioRule(ShowListActivity::class.java)
@Test
fun testItemCount(){
activityScenarioRule.scenario.onActivity { activityScenarioRule ->
    val recyclerView = activityScenarioRule.findViewById<RecyclerView(R.id.movieListRecyclerView)
    val itemCount = recyclerView.adapter?.itemCount?:0
    ....
    }
}