在使用Espresso进行测试时,如何在Android上的NavigationView中单击menuItem?

时间:2015-06-10 14:04:19

标签: android android-espresso

如何使用Espresso从设计库中单击NavigationView中的menuItem?它可以在整个菜单可见的大型设备上正常工作,但在横向或较小的设备中,我的菜单底部不在屏幕上,我无法想出滚动/滑动到这些menuItem的方法,所以我可以点击它们。

通话:

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

生成以下内容:

  

引起:java.lang.RuntimeException:不会执行操作   因为目标视图与以下一个或多个不匹配   约束:(视图有效可见性=可见并且是后代   of :(可从类中分配:类android.widget.ScrollView或是   可从类分配:class android.widget.Horizo​​ntalScrollView))   目标视图:" NavigationMenuItemView {id = -1,visibility = VISIBLE,   width = 888,height = 144,has-focus = false,has-focusable = false,   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背后的逻辑可行,但我无法让它工作。

3 个答案:

答案 0 :(得分:3)

我想通了,我使用了自定义的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)));

答案 1 :(得分:2)

即使菜单项不可见(列表中某处),这对我也有用

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

答案 2 :(得分:0)

您需要使用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());