浓缩咖啡 - 检查 RecyclerView 物品的订购是否正确

Fra*_*pos 8 android android-espresso android-recyclerview

如何使用 Espresso 检查 RecyclerView 项目是否以正确的顺序显示?我正在尝试通过文本检查每个元素的标题来测试它。

当我尝试这段代码时,它可以单击该元素,但无法继续执行单击来尝试断言该元素的文本

onView(withId(R.id.rv_metrics)).perform(actionOnItemAtPosition(0, click()));
Run Code Online (Sandbox Code Playgroud)

当我尝试使用自定义匹配器时,我不断收到错误消息

Error performing 'load adapter data' on view 'with id: mypackage_name:id/rv_metrics'
Run Code Online (Sandbox Code Playgroud)

我现在知道了,onData doesn't work for RecyclerView但在此之前我曾尝试使用自定义匹配器来完成此任务。

 public static Matcher<Object> hasTitle(final String inputString) {
    return new BoundedMatcher<Object, Metric>(Metric.class) {
        @Override
        protected boolean matchesSafely(Metric metric) {

            return inputString.equals(metric.getMetric());

        }

        @Override
        public void describeTo(org.hamcrest.Description description) {
            description.appendText("with title: ");
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

我也尝试过这样的事情,但由于作为 actionOnItemAtPosition 方法的参数给出的类型,它显然不起作用,但我们是否有类似的东西可以工作?

onView(withId(R.id.rv_metrics)).check(actionOnItemAtPosition(0, ViewAssertions.matches(withText("Weight"))));
Run Code Online (Sandbox Code Playgroud)

请问我在这里缺少什么?非常感谢。

Mos*_*ius 7

正如这里提到的,RecyclerView对象的工作方式与AdapterView对象不同,因此onData()不能用于与它们交互。

为了在 a 的特定位置找到视图,RecyclerView您需要实现RecyclerViewMatcher如下自定义:

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

            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

然后以这种方式在您的测试用例中使用它:

@Test
void testCase() {
    onView(new RecyclerViewMatcher(R.id.rv_metrics)
        .atPositionOnView(0, R.id.txt_title))
        .check(matches(withText("Weight")))
        .perform(click());

    onView(new RecyclerViewMatcher(R.id.rv_metrics)
        .atPositionOnView(1, R.id.txt_title))
        .check(matches(withText("Height")))
        .perform(click());
}
Run Code Online (Sandbox Code Playgroud)