【问题标题】:How do you click a menuItem in NavigationView on Android when testing with Espresso?使用 Espresso 进行测试时,如何在 Android 上的 NavigationView 中单击 menuItem?
【发布时间】:2015-06-10 14:04:19
【问题描述】:

如何使用 Espresso 从设计库中单击 NavigationView 中的 menuItem?它在整个菜单可见的较大设备上运行良好,但在横向或较小设备中,我的菜单底部不在屏幕上,我无法找到滚动/滑动到这些菜单项的方法,因此我可以点击它们。

调用:

onView(withText(R.string.contact_us)).perform(scrollTo()).check(matches(isDisplayed())).perform(click());

生成以下内容:

原因:java.lang.RuntimeException: Action will not be executed 因为目标视图与以下一项或多项不匹配 约束:(视图具有有效的可见性=可见并且是后代 一个:(可从类分配:类 android.widget.ScrollView 或者是 可从类分配:类 android.widget.Horizo​​ntalScrollView)) 目标视图:“NavigationMenuItemView{id=-1, visibility=VISIBLE, 宽度=888,高度=144,有焦点=假,有焦点=假, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false,is-focusable=false,is-layout-requested=false, is-selected=false,root-is-layout-requested=false, has-input-connection=false, x=0.0, y=1653.0, text=联系我们, input-type=0, ime-target=false, has-links=false}"

NavigationView 继承自 FrameLayout 并正在执行画布切换以滚动。 https://developer.android.com/reference/android/support/design/widget/NavigationView.html

我尝试编写一个 customViewAction 以使用以下操作缓慢滚动屏幕并检查 menuItem 是否可见,如果不继续滚动,但我无法让它工作。

CoordinatesProvider coordinatesProvider = new CoordinatesProvider() {
        public float[] calculateCoordinates(View view) {
            float[] xy = GeneralLocation.BOTTOM_CENTER.calculateCoordinates(view);
            xy[0] += 0.0F * (float)view.getWidth();
            xy[1] += -0.083F * (float)view.getHeight();
            return xy;
        }
    };

onView(withId(R.id.navigation_view)).perform(new GeneralSwipeAction(Swipe.SLOW,
                             coordinatesProvider, GeneralLocation.TOP_CENTER, Press.FINGER));

onView(withText(R.string.contact_us)).check(matches(isDisplayed())).perform(click());

有人知道如何在 Espresso 上测试 navigationView 吗?我很确定我的 customViewAction 背后的逻辑会起作用,但我无法让它起作用。

【问题讨论】:

    标签: android android-espresso


    【解决方案1】:

    我想通了,我使用了自定义 viewAction

    public static ViewAction swipeForTextToBeVisible(final String text, final long millis, final View navigationView) {
            return new ViewAction() {
                @Override
                public Matcher<View> getConstraints() {
                    return isRoot();
                }
    
                @Override
                public String getDescription() {
                    return "swipe for a specific view with text <" + text + "> during " + millis + " millis.";
                }
    
                @Override
                public void perform(final UiController uiController, final View view) {
                    uiController.loopMainThreadUntilIdle();
                    final long startTime = System.currentTimeMillis();
                    final long endTime = startTime + millis;
                    final Matcher<View> viewMatcher = withText(text);
    
                    do {
                        boolean returnAfterOneMoreSwipe = false; //not sure why I have to do one more swipe but it works
    
                        for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
                            // found view with required ID
                            if (viewMatcher.matches(child) && child.getVisibility() == View.VISIBLE) {
                                returnAfterOneMoreSwipe = true;
                            }
                        }
    
                        CoordinatesProvider coordinatesProvider = new CoordinatesProvider() {
                            public float[] calculateCoordinates(View view) {
                                float[] xy = GeneralLocation.BOTTOM_CENTER.calculateCoordinates(view);
                                xy[0] += 0.0F * (float)view.getWidth();
                                xy[1] += -0.083F * (float)view.getHeight();
                                return xy;
                            }
                        };
    
                        //Below code is c/p from perform in android/support/test/espresso/action/GeneralSwipeAction.class
                        float[] startCoordinates = coordinatesProvider.calculateCoordinates(navigationView);
                        float[] endCoordinates = GeneralLocation.TOP_CENTER.calculateCoordinates(navigationView);
                        float[] precision = Press.FINGER.describePrecision();
                        Swiper.Status status = Swiper.Status.FAILURE;
    
                        for(int tries = 0; tries < 3 && status != Swiper.Status.SUCCESS; ++tries) {
                            try {
                                status = Swipe.SLOW.sendSwipe(uiController, startCoordinates, endCoordinates, precision);
                            } catch (RuntimeException var9) {
                                throw (new PerformException.Builder()).withActionDescription(this.getDescription()).withViewDescription(HumanReadables.describe(navigationView)).withCause(var9).build();
                            }
    
                            int duration = ViewConfiguration.getPressedStateDuration();
                            if(duration > 0) {
                                uiController.loopMainThreadForAtLeast((long)duration);
                            }
                        }
                        //End c/p from android/support/test/espresso/action/GeneralSwipeAction.class
    
                        if (returnAfterOneMoreSwipe) {
                            return;
                        }
    
                        uiController.loopMainThreadForAtLeast(50);
                    }
                    while (System.currentTimeMillis() < endTime);
    
                    // timeout happens
                    throw new PerformException.Builder()
                            .withActionDescription(this.getDescription())
                            .withViewDescription(HumanReadables.describe(view))
                            .withCause(new TimeoutException())
                            .build();
                }
            };
        }
    

    我不喜欢这个解决方案,它不是最干净的,因为由于某种原因,我必须在它说它可见但它有效之后再次滑动。

    要使用它,您可以通过以下方式调用它:

    onView(isRoot()).perform(CustomViewActions.swipeForTextToBeVisible(getActivity().getString(R.string.contact_us),2000, getActivity().findViewById(R.id.navigation_view)));
    

    【讨论】:

      【解决方案2】:

      即使菜单项不可见(在列表中的某个位置),这对我也有用

      onView(withId(R.id.nav_view)).perform(NavigationViewActions.navigateTo(R.id.nav_login));

      【讨论】:

      • 单行就像一个魅力!谢谢。
      【解决方案3】:

      您需要使用 espresso-contrib:

      import android.support.test.espresso.contrib.DrawerActions;
      

      要打开菜单,您可以这样做:

      onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
      

      您可以向下滚动到菜单项并检查它是否显示:

      onView(withId(R.id.my_menu_item)).perform(scrollTo()).check(matches(isDisplayed()));
      

      然后点击菜单项:

      onView(withId(R.id.my_menu_item)).perform(click());
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多