【问题标题】:How to check if a View is VISIBLE on a specific RecyclerView item?如何检查特定 RecyclerView 项目上的视图是否可见?
【发布时间】:2016-09-25 14:59:15
【问题描述】:

我不熟悉使用浓缩咖啡。使用此代码我无法真正得到我想要测试的内容:

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

其中R.id.imageview_tagR.id.relativelayout_tag 的子代。 R.id.relativelayout_tag 包含我的适配器项目的全部内容。 R.id.recyclerview_tag_list 是我的 RecyclerView 的名称,我在其上分配了一个特定的 RecyclerView Adapter

这是一个非常非常基础的测试。以下是用户程序:

  1. 选择,SPECIFICALLYRecyclerView 上的第一项(我真的不在乎 文本在视图上)。也不建议使用视图文本来识别第一项。 我不关心适配器项目的内容,甚至在某些视图上放置一个独特的标签。
  2. 在选择时,指示器视图(显示项目的 ImageView 被选中)应该会出现。

非常基本和简单。使用 Espresso 为这个基本的用户故事编写测试非常困难。当我运行该特定测试时,它总是失败,说明:

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

这没有意义,因为我的列表已经可见。我什至可以很好地运行这个测试:

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

这是完整的测试:

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

如果指示器视图的可见性设置为visible仅在该特定项目上(或我选择的任何项目),我想测试什么。

有什么想法吗?也许我错过了一些东西。

非常感谢!

【问题讨论】:

    标签: android unit-testing android-espresso


    【解决方案1】:

    onData 不适用于 RecyclerView,因为 RecyclerView 不能扩展 AdapterView

    您需要使用onView 来进行断言。如果它是回收器视图中的第一项,那么您可以使用类似这个匹配器的东西来进行断言:

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

    而用法如下:

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

    请记住,如果您的 ViewHolder 尚未布局,则此匹配器将失败。如果是这种情况,您需要使用RecyclerViewActions 滚动到 ViewHolder。如果您在使用匹配器之前单击测试中的项目,则无需滚动。

    【讨论】:

    • 您好,我编辑了您的答案。应该是onView(withId(R.id.recyclerview_tag_list)),我知道这就是你的意思。谢谢,我现在可以通过考试了。我仍然有点难以理解 BoundedMatchers。如果可以的话,我会尽量避免它们。好像不是这种情况。
    【解决方案2】:

    检查不可见的元素线性布局时出现异常错误。为了解决它,我不得不添加 withFailureHandler 如下:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-30
      • 2011-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多