【问题标题】:Android Expresso verifying Context Menu and actionBar itemsAndroid Espresso 验证上下文菜单和操作栏项目
【发布时间】:2017-06-20 00:42:23
【问题描述】:

我有一个列表,其中每一行都包含一个名称和一个调用上下文菜单选项的按钮。我想编写一个测试来验证以下内容

  1. 上下文菜单包含正确数量的项目
  2. 上下文菜单包含正确的值
  3. 上下文菜单不包含任何不必要的选项(上面的检查 1 和 2 将测试这种情况)

我也想测试一下长选时actionBar和actionBar溢出菜单的内容。

对于这两个测试,我可以编写一个检查以确保有一个显示正确“标签”的视图元素(即使用 onView(withText(this.elementText)) 查找视图。但是我有 2 个操作具有标签相同但 ID 不同,我需要确保上下文菜单/长按菜单中的操作正确。

我不能将我在 XML 中指定的 ID 用于上下文菜单的菜单,因为 Android 的上下文菜单视图没有这些 ID,而是包含一个内部 Android ID(参见下面的屏幕截图)。

当我使用 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;
}

有谁知道我如何使用 Expresso 获取 Context Row 菜单项列表。

有谁知道我如何使用 Expresso 获取 actionBar 项目(包括溢出菜单中的所有项目)?

【问题讨论】:

    标签: android expresso


    【解决方案1】:

    如果我正确理解您的问题,您应该可以使用 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))。但是我有两个动作 具有相同的标签但不同的 ID,我需要确保正确 操作在上下文菜单/长按菜单中。

    atPosition 应该会为您提供您对列表感兴趣的视图,如果您需要在列表中的给定项目中定位为子视图,您可以添加onChildView

    希望有帮助!

    【讨论】:

    • 感谢您的快速答复。我一直在使用您建议的代码,但遇到了问题。 onView(..) 返回“android.widget.PopupWindow$PopupDecorView”,因此“onView(isAssignableFrom(AdapterView.class))”失败,因为我似乎没有获得可从适配器视图分配的视图跨度>
    • 澄清你说你不清楚的部分。菜单项在 xml 文件布局中具有标题和 ID。我可以检查菜单项的文本是否与 xml 布局文件中的标题相同(您的示例代码就是这样做的)。但我需要使用 xml 布局文件中的 ID 进行测试。我看不到如何从菜单项中获取此 ID,因为“ListMenuItemView”包含其 ID 不是 xml 布局文件中的 ID 的子元素
    • 使用onView(isAssignableFrom(AdapterView.class)) 的建议未经测试,我只是假设该类会匹配。我要做的是查看失败吐出的日志(“期望这样那样的视图但得到这样那样的视图,这是层次结构”的长转储 - 找到您要匹配的视图( popup). 你可以试试onView(both(isRoot()).and((not(is(getActivity().getWindow().getDecorView())))));(即不是主Activity根视图的根视图,即popup的根视图)
    • 关于菜单 ID,我认为您无法通过 Espresso 对它们进行任何操作,正如您所注意到的。与菜单交互的文档说只使用菜单项上的文本。但你为什么关心他们?您应该能够通过它的文本和/或列表中的位置来单击并断言给定的菜单项。
    • Menu Ids:我有 2 个带有字符串“Open”的操作,如果用户拥有该项目,则“Open”将打开“screen1”,如果用户不拥有该项目,则“打开”将打开“screen2”。因此,我有 2 个标签相同但 ID 不同的操作,例如“Open”/R.id.open_for_owner 和“Open”/R.id.open_for_non_owner。我想检查用户拥有的文件的操作是否具有“打开”/R.id.open_for_owner 操作而不是“打开”/R.id.open_for_non_owner 操作。在我的测试中,我纯粹是想测试动作列表的内容,我不想点击每个动作来验证它们。
    【解决方案2】:

    非常感谢dominicoder 给了我这个问题的答案。根据他们的回复,我已经设法让它发挥作用。

    我不使用“isAssignableFrom(AdapterView.class)”,而是使用“isAssignableFrom(ListView.class)”。

    然后我使用提供的完全相同的匹配器“dominicoder”来验证上下文菜单项计数。

    使用“dominicoder's”示例匹配器,我自己编写了一个代码,用于检查 ListView 中某个位置的 MenuItem,我可以比较 ID 以确保它是我期望的 ID。

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

    使用此代码,我可以检查上下文菜单包含正确数量的菜单项,并且菜单中的项目是我期望的(使用 ID 验证)以及我期望的顺序。

    非常感谢“dominicoder”。很遗憾,您无法将两个答案都标记为正确,因为实际上您实际上给了我正确的答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-30
      • 1970-01-01
      相关资源
      最近更新 更多