41

I have a menu in the actionbar which I create through:

@Override
public boolean onCreateOptionsMenu(Menu menu) {

    menu.add(Menu.NONE, 98,Menu.NONE,R.string.filter).setIcon(R.drawable.ic_filter_list_white_48dp).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    menu.add(Menu.NONE, 99,Menu.NONE,R.string.add).setIcon(R.drawable.ic_add_white_48dp).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);


    getMenuInflater().inflate(R.menu.menu_main, menu);

    return true;
}

and menu_main.xml looks like:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">
    <item
        android:id="@+id/action_settings"
        android:title="@string/action_settings"
        android:orderInCategory="100"
        app:showAsAction="never"
        android:icon="@drawable/ic_settings_white_48dp"/>
</menu>

When testing in Espresso I would like to click on the "add" icon (menuId 99). I tried

@Test
public void testAdd() {
openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext());
    onView(withText(R.string.add)).perform(click());
}

but this fails with a NoMatchingViewException. ( The settings item, which is defined in the xml directly I can click with the same code. )

That's for targetSdkVersion 23 and AppCompatActivity. The relevant lines for the tool bar are:

Toolbar toolbar = (Toolbar) findViewById(R.id.tool_bar);
setSupportActionBar(toolbar);
if( getSupportActionBar() != null ) {
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}

and tool_bar.xml looks like:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar     xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    android:background="@color/ColorPrimary"
    android:elevation="4dp"
    tools:ignore="UnusedAttribute">
</android.support.v7.widget.Toolbar>
albodelu
  • 7,931
  • 7
  • 41
  • 84
user1583209
  • 1,637
  • 3
  • 24
  • 42
  • 1
    What `targetSdkVersion`, ActionBar and device/emulator are you using?. See the openActionBarOverflowOrOptionsMenu() implementation (Espresso class), depends on these factors. I need to investigate it but there is something wrong there depending on the emulator I use. – albodelu Nov 28 '15 at 15:17
  • targetSdkVersion 23 and AppCompatActivity. I added the toolbar code above. – user1583209 Nov 28 '15 at 21:21
  • Could there be a problem when matching the text with "withText(R.string.add)", as the actual text is not visible, but only the icon (R.drawable.ic_add_white_48dp) is visible? – user1583209 Nov 28 '15 at 21:24
  • That's on a Sony Xperia SP (real device, not emulator). – user1583209 Nov 28 '15 at 23:57
  • Not a solution, but I noticed that "onView(withId(99)).perform(click());" works, even though I remember reading somewhere that those "menu-Id" are not the same as the "view-id".... – user1583209 Nov 29 '15 at 22:18
  • I deleted my comments because I didn't see the final of the lines and that changes it, sorry. I would split them, so other like me see it. – albodelu Nov 30 '15 at 04:01

7 Answers7

56

Update: I didn't see the final of the line, ignore my previous responses, I tried to fix it fast and I did wrong. I really didn't know the answer.

...setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)

Your question is explained here by the Espresso team:

Matching visible icons:

  // Click on the icon - we can find it by the r.Id.
  onView(withId(R.id.action_save))
    .perform(click());

Clicking on items in the overflow menu is a bit trickier for the normal action bar as some devices have a hardware overflow menu button (they will open the overflowing items in an options menu) and some devices have a software overflow menu button (they will open a normal overflow menu). Luckily, Espresso handles that for us.

  // Open the overflow menu OR open the options menu,
  // depending on if the device has a hardware or software overflow menu button.
  openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());

  // Click the item.
  onView(withText("World"))
    .perform(click());

So I understand that both alternatives are correct but depend on your specific case. If you test a button you normally know if it's visible or not at that moment.

In your case the button is visible because there is room, and it's correct to use withId like here:

import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;

@Test
public void testClickInsertItem() {
    openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext());
    onView(withId(R.id.action_insert)).perform(click());
}

But this it's correct too for overflow items:

Yes, this is how it works in Espresso. The problem here is, that in Android, the View representing the menu item doesn't have the ID of the menu item. So onView(withId(X)) just fails to find a View. I don't have a better recommendation than just using withText(). If you have multiple views with the same text, using hierarchy for distinction works.

Now my question is what to do when you test on different devices and you don't know when will be room for an item. I don't know the response, perhaps combine both solutions.

Community
  • 1
  • 1
albodelu
  • 7,931
  • 7
  • 41
  • 84
  • Thanks. So it is not possible to match a visible icon using "withText()"? If possible I would like to avoid using "withId()" as much as possible, since in my understanding the test should imitate user actions and humans would look for a text/icon rather than for an (invisible) id. – user1583209 Nov 30 '15 at 11:19
  • I dont know, wait a better response, I answered because i thought it was the same issue that I commented you. I'll update it if nobody answer and I find a better response. I never used SHOW_AS_ACTION_IF_ROOM and I'm still learning Espresso too. – albodelu Nov 30 '15 at 15:38
7

This is my solution which covers all the three situations: hardware menu button, overflow menu, only icons:

    try {
        openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext());
    } catch (Exception e) {
        //This is normal. Maybe we dont have overflow menu.
    }
    onView(anyOf(withText(R.string.<your label for the menu item>), withId(R.id.<id of the menu item>))).perform(click());
guni
  • 312
  • 2
  • 9
6

Use this to match on the description text for the menu item:

onView(withContentDescription(R.string.add)).perform(click());

Then you do not have to match on the (non-existent) ID, nor on the drawable icon.

Also, remember to use this code snippet from the Espresso documentation if you need to make sure the menu is expanded:

// Open the overflow menu OR open the options menu,
// depending on if the device has a hardware or software overflow menu button.
openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());
Eirik W
  • 3,086
  • 26
  • 29
  • This is the best solution for all cases where withText() is not applicable! It also works for icon-only items (app:showAsAction="always"). Just add ` – allofmex Apr 08 '21 at 14:13
5

Don't suffer a lot. Let's Make it simple.

onView(withId(98)).perform(click());

This will help you to perform click on Add

Skandix
  • 1,916
  • 6
  • 27
  • 36
Manish
  • 51
  • 1
  • 3
  • This I know. But I'd prefer to match "withText()". Also see my comment to albodelu's answer. – user1583209 Mar 06 '18 at 16:29
  • With Text will not suit to this scenario because Espresso basically find the text which has been shown on the Screen. However we using Icon which doesn't have any text. We can't perform action with Text(). – Manish Mar 07 '18 at 05:40
4

You can use this method to click menu item. First click menu button using this code

openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());

Then perform click menu item based on text.Replace your menu item name with "MenuItemName"

onView(withText("MenuItemName")).perform(click());
Ishan Fernando
  • 2,758
  • 1
  • 31
  • 38
1
Espresso.openContextualActionModeOverflowMenu() 

was the only option that worked for my Samsung Note 3. This openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext()); would try and fail without any error

HRVHackers
  • 2,793
  • 4
  • 36
  • 38
-1

The toolbar is not the actionBar, no need of the call :

openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext());

And if you do it, that's sure your item won't be found there (it's not in the ActionBar) and the ToolBar belongs to the "normal view".