Android Expresso проверяет контекстное меню и элементы actionBar

У меня есть список, где каждая строка содержит имя и кнопку, которая вызывает контекстное меню параметров. Я хотел бы написать тест, который проверяет следующие вещи

  1. контекстное меню содержит правильное количество пунктов
  2. контекстное меню содержит правильные значения
  3. контекстное меню не содержит никаких необоснованных опций (проверки 1 и 2 выше проверят этот случай)

Я также хотел бы проверить содержимое меню переполнения actionBar и actionBar, когда элемент долго выбран.

Для обоих тестов я могу написать проверку, которая гарантирует, что есть элемент представления с отображенной правильной "меткой" (то есть, находим представление, используя onView(withText(this.elementText)). Однако у меня есть 2 действия, которые имеют одинаковую метку разные идентификаторы, и я должен убедиться, что правильное действие находится в контекстном меню / меню длинного щелчка.

Я не могу использовать идентификатор, указанный в XML, для меню моего контекстного меню, поскольку представления контекстного меню Android не имеют этих идентификаторов, вместо этого они содержат внутренний идентификатор Android (см. Скриншот ниже). введите описание изображения здесь

Когда я писал тесты с использованием Robotium, мне нужно было получить все текущие представления определенного типа и проанализировать их, проверяя, являются ли они элементами actionBar, см. Пример кода ниже.

public static List<MenuItemImpl> getLongClickMenuItems(String itemName) {
    List<MenuItemImpl> menuItems = new ArrayList<>();

    // long select the item
    solo.clickLongOnText(itemName);

    // get the children of the of the long click action bar
    ArrayList<ActionMenuView> outViews = solo.getCurrentViews(ActionMenuView.class, solo.getView(R.id.action_mode_bar));

    if (!outViews.isEmpty()) {
        // get the first child which contains the action bar actions
        ActionMenuView actionMenuView = outViews.get(0);
        // loop over the children of the ActionMenuView which is the individual ActionMenuItemViews
        // only a few fit will fit on the actionBar, others will be in the overflow menu
        int count = actionMenuView.getChildCount();
        for (int i = 0; i < count; i++) {
            View child = actionMenuView.getChildAt(i);

            if (child instanceof ActionMenuItemView) {
                menuItems.add(((ActionMenuItemView) child).getItemData());
            } else {
                // this is the more button, click on it and wait for the popup window
                // which will contain a list of ListMenuItemView
                // As we are using the AppCompat the actionBar's menu items are the
                // the AppCompat's ListMenuItemView (android.support.v7.view.menu.ListMenuItemView)
                // In the context menu, the menu items are Android's native ListMenuItemView
                // (com.android.internal.view.menu.ListMenuItemView)
                solo.clickOnView(child);
                solo.waitForView(ListMenuItemView.class);
                ArrayList<ListMenuItemView> popupItems = solo.getCurrentViews(ListMenuItemView.class);
                for (ListMenuItemView lvItem : popupItems) {
                    menuItems.add(lvItem.getItemData());
                }

                // close the more button actions menu
                solo.goBack();
            }
        }
    }

    // get out of long click mode
    solo.goBack();

    return menuItems;
}

Кто-нибудь знает, как я могу получить список пунктов меню Context Row с помощью Expresso.

Кто-нибудь знает, как я могу получить элементы actionBar (включая все элементы в меню переполнения) с помощью Expresso?

2 ответа

Решение

Большое спасибо dominicoder за то, что он дал мне ответ на этот вопрос. работая над их ответом, мне удалось заставить его работать.

Вместо использования "isAssignableFrom(AdapterView.class)" я использую "isAssignableFrom(ListView.class)".

Затем я использую то же самое соответствие "dominicoder", чтобы проверить количество пунктов контекстного меню.

Используя примеры соответствия "dominicoder", я смог сам написать код, который проверяет MenuItem в определенной позиции в ListView, и я могу сравнить идентификаторы, чтобы убедиться, что это ожидаемый идентификатор.

public boolean verifyRowContextMenuContents(String name, MyActionObject[] actions){
    // invoke the row context menu
    clickRowActionButton(name);

    // The Context Menu Popup contains a ListView
    int expectedItemCount = actions.length;

    // first check the Popup's listView contains the correct number of items
    onView(isAssignableFrom(ListView.class))
            .check(matches(correctNumberOfItems(expectedItemCount)));

    // now check the order and the IDs of each action in the menu is the expected action
    for (int i = 0; i < expectedItemCount; i++) {
        onView(isAssignableFrom(ListView.class))
                .check(matches(correctMenuId(i, actions[i].getId())));
    }

    // close the context menu
    pressBack();

    return true;
}

private static Matcher<View> correctNumberOfItems(final int itemsCount) {
    return new BoundedMatcher<View, ListView>(ListView.class) {
        @Override
        public void describeTo(Description description) {
            description.appendText("with number of items: " + itemsCount);
        }

        @Override
        protected boolean matchesSafely(ListView listView) {
            ListAdapter adapter = listView.getAdapter();
            return adapter.getCount() == itemsCount;
        }
    };
}

private static Matcher<View> correctMenuId(final int position, final int expectedId) {
    return new BoundedMatcher<View, ListView>(ListView.class) {
        @Override
        public void describeTo(Description description) {
            description.appendText("with position : " + position + " expecting id: " + expectedId);
        }

        @Override
        protected boolean matchesSafely(ListView listView) {
            ListAdapter adapter = listView.getAdapter();
            Object obj = adapter.getItem(position);
            if (obj instanceof MenuItem){
                MenuItem menuItem = (MenuItem)obj;
                return menuItem.getItemId() == expectedId;
            }
            return false;
        }
    };
}   

С помощью этого кода я могу проверить, что Контекстное меню содержит правильное количество пунктов меню, и что пункты в меню - это те, которые я ожидаю (с помощью проверки идентификатора), и порядок, который я ожидаю.

Огромное спасибо "Доминикодеру". Жаль, что вы не можете пометить оба ответа как правильные, поскольку вы фактически дали мне практически правильный ответ.

Если я правильно понимаю ваш вопрос, вы сможете использовать метод onData() для взаимодействия с контекстными меню, поскольку они просто AdapterViews (обратите внимание на скриншот, всплывающее окно ListPopupWindow$DropDownListView).

Таким образом, вы должны быть в состоянии сделать что-то вроде этого:

onView(withText("Item Label")).perform(longClick());
final int expectedItemCount = 10;

// Now the floating popup should be showing,
// first assert it has the expected item count

onView(isAssignableFrom(AdapterView.class))
.check(matches(withItemCount(expectedItemCount)));

// Now assert each entry (which should just be a string) is shown correctly
for (int i = 0; i < expectedItemCount; i++) {
    String expectedItemText = getExpectedItemTextForIndex(i);
    onData(instanceOf(String.class)).atPosition(i)
        .check(matches(withText(expectedItemText)));
}

Где withItemCount Приведенное выше совпадение - это простое сопоставление, которое утверждается в зависимости от количества адаптеров данного представления адаптера:

public static Matcher<View> withNumberOfItems(final int itemsCount) {
    return new BoundedMatcher<View, AdapterView>(AdapterView.class) {
        @Override
        public void describeTo(Description description) {
            description.appendText("with number of items: " + itemsCount);
        }

        @Override
        protected boolean matchesSafely(AdapterView item) {
            return item.getAdapter().getCount() == itemsCount;
        }
    };
}

Я думаю, что та же самая концепция должна работать для меню переполнения панели действий.

Мне не понятно, что вы имеете в виду под:

Для обоих тестов я могу написать проверку, которая гарантирует, что есть элемент представления с отображенной правильной "меткой" (то есть, находим представление, используя onView(withText(this.elementText)). Однако у меня есть 2 действия, которые имеют одинаковую метку, но разные идентификаторы, и я должен убедиться, что правильное действие находится в контекстном меню / меню длинного щелчка.

Но atPosition должен получить именно тот вид, который вас интересует в списке, и вы можете добавить onChildView если вам нужно настроить таргетинг как на подэлемент внутри данного элемента в списке.

Надеюсь, это поможет!

Другие вопросы по тегам