How to test ActionMenuItemView's icon in Espresso
K

2

5

I have a button in action bar, for which the icon is changed depending of a boolean. I would like to check which drawable resource is used.

Here is the code where the icon is changed:

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

When icon needs to be changed, the menu is invalidated:

// request menu update
supportInvalidateOptionsMenu();

Finally, my espresso code where I would like to check the result:

@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()));

Please note that I am using a custom matcher found here.

Kastroprauxel answered 2/12, 2015 at 22:5 Comment(0)
A
8

I'm not a 100% sure on how the matchers work and whether this is the best response but using a slightly different version of the method certainly works.

The problem is that the current matcher only works with ImageViews. ActionMenuItemView actually subclasses textView so won't match and it also has no method for getDrawable().

Please note, this still requires the sameBitmap method from the original post.

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);
        }
    };
}
Armil answered 3/12, 2015 at 10:47 Comment(3)
Note : this also works with a BottomNavigationView, using BottomNavigationItemView instead of ActionMenuItemViewAntislavery
How to perform a click on this exact item?Burhans
@Jack' I'm doing with BottomNavigationItemView also and I'd like to know how can I click on an item found inside that matcherBurhans
L
0

The answer by @Barry Irvine is really helpful. Just wanted to clarify if anyone wonders how to use the given Matcher method in an Espresso test. (Kotlin)

Step 1: Create a new file CustomMatchers from this link mentioned in the question. (include the sameBitmap method, however, looking at the comments, modified the sameBitmap method)

You could directly add the method within your test file, but adding it in a different file would help in re-using it whenever you need to test menu item icons.

For reference, this is how my CustomMatchers file looks like

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

Step 2: Using the matcher in a test

 onView(withId(R.id.menu_item_id))
        .check(matches(CustomMatchers.withActionIconDrawable(R.drawable.ic_favorite_border)))
Lipo answered 25/11, 2021 at 9:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.