Espresso - 如何点击底部导航栏的特定项目

问题描述

我试图点击我的 BottomNavigationBar 中的一个项目,但即使我记录了一个 Espresso 测试,然后将其放入我的测试中,它也找不到 onView

Espresso 测试给出的记录为我写的是:

private fun childAtPosition(
        parentMatcher: Matcher<View>,position: Int
    ): Matcher<View> {

        return object : TypeSafeMatcher<View>() {
            override fun describeto(description: Description) {
                description.appendText("Child at position $position in parent ")
                parentMatcher.describeto(description)
            }

            public override fun matchesSafely(view: View): Boolean {
                val parent = view.parent
                return parent is ViewGroup && parentMatcher.matches(parent)
                    && view == parent.getChildAt(position)
            }
        }
    }


    val bottomNavigationItemView = onView(
            allOf(
                withContentDescription("Home"),childAtPosition(
                    childAtPosition(
                        withId(R.id.navigation_bar),0
                    ),1
                ),isdisplayed()
            )
        )
        bottomNavigationItemView.perform(click())

即使我尝试这样做,它也会说:

androidx.test.espresso.PerformException:在视图上执行'单击'时出错'(内容描述文本:是“Home”,子节点在父节点中的位置 1 子节点位于父节点的位置 0,id 为 显示在屏幕上给用户)'。

我尝试了多种方法,使用 BoundedMatcher 但我无法让它工作。我错过了什么?

fun withTitle(titleTested: String): Matcher<View?>? {
        return object : BoundedMatcher<View?,BottomNavigationItemView>(
            BottomNavigationItemView::class.java
        ) {
            private var triedMatching = false
            private var title: String? = null
            override fun describeto(description: Description) {
                if (triedMatching) {
                    description.appendText("with title: $titleTested")
                    description.appendText("But was: " + title.toString())
                }
            }

            override fun matchesSafely(item: BottomNavigationItemView): Boolean {
                triedMatching = true
                title = item.itemData.title.toString()
                return title == titleTested
            }
        }
    }

我的xml的层次结构是:

CoordinatorLayout(id : mainCordinator)
   RelativeLayout (no id)
      com.google.android.material.bottomnavigation.BottomNavigationView(id : navigation_bar) />
         Coordinatorlayout(id: anotherCoordinator)
           FrameLayout(id: framelayout)
           FloatActionButton(id: fab_test)
         />
   />  
/>

我想知道最简单的方法,这样我就可以调用一个传递底部导航的索引函数并点击它,或者甚至将内容描述/标题/文本作为参数发送,无论是的。

我正在按如下方式动态创建项目:

        ArrayList<String> items...
        ....Create....
        Menu menu = binding.navigationBar.getMenu();
        menu.clear();
        for (int i = 0; i < items.size(); i++) {
            menu.add(
                0,items.get(i),i,bottomBarTitles.get(i));
            menu.getItem(i).setIcon(bottomBarImages.getItemIcon(items.get(i)));
            bottomMenu.add(items.get(i));

Items 是一个 ArrayList<String>,例如“首页内容图片、更多” 然后我有一个带有图像的 ArrayList

编辑

我可以使用 UiDeviceUiSelector 让它工作,但我想用 Espresso 来做这个。

val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
            device.findobject(UiSelector().text("Home")).click()

解决方法

你能试试这个吗:

onView( allOf( withText("Home),isDescendantOfA(withId(R.id.navigation_bar)) ).perform( click() )

基本上,我们正在寻找带有文本“Home”的视图,它也恰好是带有底部栏 ID 的视图的后代。

,

我认为使用 withID 会适合您,我知道 espresso 记录的测试是不稳定的,最好编写自己的代码。

public static void main(String[] args) throws IOException {
    try(ICsvBeanReader beanReader = new CsvBeanReader(new FileReader("source.csv"),CsvPreference.STANDARD_PREFERENCE))
    {       
      String[] headers = new String[]{"CustomerId","CustomerName","Email"};
      ICsvListWriter writer = new CsvListWriter(new PrintWriter("other.csv"),CsvPreference.STANDARD_PREFERENCE);
      writer.writeHeader(headers);
    }
}

我不确定您为导航项分配了哪些 ID,所以我使用了 home。

如果多个视图具有相同的 ID,您可以使用。

onView(withId(R.id.home).perform(click())

如果您单独使用 onView(withId(R.id.home),withText("Home")).performClick() ,它将尝试对所有包含“主页”一词的视图执行点击操作。