如何在 Espresso 中测试 ActionMenuItemView 的图标

How to test ActionMenuItemView's icon in Espresso

我在操作栏中有一个按钮,其图标根据布尔值更改。我想检查使用了哪个可绘制资源。

这是更改图标的代码:

@Override
public void onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);
    MenuItem item = menu.findItem(R.id.menu_favorite);
    if(mIsFavorite)
        item.setIcon(R.drawable.ab_icon_on);
    else
        item.setIcon(R.drawable.ab_icon_off);
}

需要更换图标时,菜单失效:

// request menu update
supportInvalidateOptionsMenu();

最后,我想检查结果的浓缩咖啡代码:

@Test
public void action_setUnsetFavorite() {
    // check favorite off
    onView(withImageDrawable(R.drawable.ab_icon_off))
            .check(matches(isDisplayed()));

    // click favorite button
    onView(withId(R.id.menu_favorite))
            .perform(click());

    // check favorite on
    onView(withImageDrawable(R.drawable.ab_icon_on))
            .check(matches(isDisplayed()));

请注意,我使用的是找到的自定义匹配器 here

我不能 100% 确定匹配器的工作原理以及这是否是最佳响应,但使用略有不同版本的方法肯定有效。

问题是当前的匹配器只适用于 ImageViews。 ActionMenuItemView 实际上是 textView 的子类,所以不会匹配,它也没有 getDrawable() 方法。

请注意,这仍然需要原始 post 中的 sameBitmap 方法。

public static Matcher<View> withActionIconDrawable(@DrawableRes final int resourceId) {
    return new BoundedMatcher<View, ActionMenuItemView>(ActionMenuItemView.class) {
        @Override
        public void describeTo(final Description description) {
            description.appendText("has image drawable resource " + resourceId);
        }

        @Override
        public boolean matchesSafely(final ActionMenuItemView actionMenuItemView) {
            return sameBitmap(actionMenuItemView.getContext(), actionMenuItemView.getItemData().getIcon(), resourceId);
        }
    };
}

@Barry Irvine 的回答很有帮助。只是想澄清是否有人想知道如何在 Espresso 测试中使用给定的 Matcher 方法。 (科特林)

步骤 1:从问题中提到的 link 创建一个新文件 CustomMatchers。 (包括sameBitmap方法,但是,查看注释,修改了sameBitmap方法)

您可以直接在您的测试文件中添加该方法,但将它添加到不同的文件中将有助于在您需要测试菜单项图标时重新使用它。

作为参考,这是我的 CustomMatchers 文件的样子

object CustomMatchers {

fun withActionIconDrawable(@DrawableRes resourceId: Int): Matcher<View?> {
    return object : BoundedMatcher<View?, ActionMenuItemView>(ActionMenuItemView::class.java) {
        override fun describeTo(description: Description) {
            description.appendText("has image drawable resource $resourceId")
        }

        override fun matchesSafely(actionMenuItemView: ActionMenuItemView): Boolean {
            return sameBitmap(
                actionMenuItemView.context,
                actionMenuItemView.itemData.icon,
                resourceId,
                actionMenuItemView
            )
        }
    }
}

private fun sameBitmap(
    context: Context,
    drawable: Drawable?,
    resourceId: Int,
    view: View
): Boolean {
    var drawable = drawable
    val otherDrawable: Drawable? = context.resources.getDrawable(resourceId)
    if (drawable == null || otherDrawable == null) {
        return false
    }

    if (drawable is StateListDrawable) {
        val getStateDrawableIndex =
            StateListDrawable::class.java.getMethod(
                "getStateDrawableIndex",
                IntArray::class.java
            )
        val getStateDrawable =
            StateListDrawable::class.java.getMethod(
                "getStateDrawable",
                Int::class.javaPrimitiveType
            )
        val index = getStateDrawableIndex.invoke(drawable, view.drawableState)
        drawable = getStateDrawable.invoke(drawable, index) as Drawable
    }

    val bitmap = getBitmapFromDrawable(context, drawable)
    val otherBitmap = getBitmapFromDrawable(context, otherDrawable)
    return bitmap.sameAs(otherBitmap)
}

private fun getBitmapFromDrawable(context: Context?, drawable: Drawable): Bitmap {
    val bitmap: Bitmap = Bitmap.createBitmap(
        drawable.intrinsicWidth,
        drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
    )
    val canvas = Canvas(bitmap)
    drawable.setBounds(0, 0, canvas.width, canvas.height)
    drawable.draw(canvas)
    return bitmap
} }

第 2 步:在测试中使用匹配器

 onView(withId(R.id.menu_item_id))
        .check(matches(CustomMatchers.withActionIconDrawable(R.drawable.ic_favorite_border)))