如何使用Espresso计算RecyclerView中的项数

71

使用Espresso和Hamcrest,如何计算recyclerView中可用项的数量?

例如:我想检查特定的RecyclerView是否显示了5个项目(必要时滚动)。


与此答案相关的一个回答。 - Sarvesh Hon
8个回答

114

这里是一个检查RecyclerView项目数量的示例ViewAssertion

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));
  }
}

然后使用这个断言

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

我已经开始编写一个库,旨在通过Espresso和UIAutomator使测试更加简单。这包括用于RecyclerView操作和断言的工具。 https://github.com/nenick/espresso-macchiato 例如,查看具有方法assertItemCountIs(int)的EspRecyclerView。


这个测试总是通过的。我认为它没有正常工作。 - AdamHurwitz
adapter.getItemCount() 返回 null 值了。您能告诉我原因吗? - Vikash Kumar
@AdamHurwitz 很多人已经确认这个工作,但请解释一下你的情况,在哪里它总是通过。 - nenick
@VikashKumar 返回的是null还是零?不应该是null。但也许这是一个时间问题,你的适配器在检查时没有任何项?这种情况经常发生在使用后台任务时。 - nenick
4
怎样计算物品的数量,而不是仅仅确认有多少个物品? - the_prole
显示剩余3条评论

36

@Stephane的回答中添加一些语法糖。

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);
    }
}

使用方法:

    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)));
    // ...

太棒了。我建议将这个功能添加到Espresso中,因为没有这样一个核心功能实在是太奇怪了。 - Samuel
不再工作了。显示一个白屏,没有更多的响应被收集,最终测试失败。 - Karunesh Palekar

21
为了完善nenick的答案并提供更灵活的解决方案,也可以测试item数量是否大于、小于等等…
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);
    }

}

使用方法:

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));
// ...

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)))

your_recycler_view_id替换为您的id,将2替换为要断言的数字。


IDE 说:无法解析方法 hasChildCount(int) - A. K. M. Tariqul Islam
1
不要忘记导入 androidx.test.espresso.matcher.ViewMatchers.hasChildCount - DamienL
2
这个计数是指可以显示的总项目数(即适配器的getItemCount()),还是指当前在RecyclerView中回收的视图数量? - Michael Osofsky
2
好问题。可能不是适配器的 getItemCount()。因此,只有在您可以拥有有限数量的项目并且不在所有设备上时,此解决方案才是可行的。 - DamienL

12

基于 @Sivakumar Kamichetty 的回答:

  1. 需要在内部类中访问变量'COUNT'时,必须将其声明为final。
  2. 不必要的行:COUNT = 0;
  3. 将变量COUNT转移为一个元素数组。
  4. 变量result是不必要的。

虽然不太好看,但能运行:

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];
}

10
你可以创建一个自定义的 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
            }
        }
    }
}

然后像这样使用:

onView(withId(R.id.recycler_view)).check(matches((hasItemCount(5))))

9
我用以下方法获取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;
    }

使用方法 -

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

然后执行断言以检查itemsCount是否符合预期


-2

使用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
    ....
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接