48

I am trying to click the home icon in some Espresso tests via:

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

This works fine for Android > 3.0 - but fails for older versions as appcompat does not seem to use this id for this element then. What is a good approach to do what I want to do?

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
ligi
  • 39,001
  • 44
  • 144
  • 244
  • Wondering if you found an answer to this? – Matt Logan Nov 12 '14 at 22:55
  • AFAIR not really - I think I worked around this then. Might be worth a try if this changed in the most recent appcompat though – ligi Nov 12 '14 at 23:00
  • Just found a solution (for me at least). See answer. – Matt Logan Nov 12 '14 at 23:07
  • @ligi Hello! Can you change the accepted answer, please? – sunlover3 Dec 07 '17 at 15:18
  • @sunlover3 ack - just changed the accepted answer to yours - you are right - this solution is better. Does it work for appcompat and native? – ligi Dec 07 '17 at 17:01
  • @ligi Great! Thank you. It should work, because the OS developers who work on this part should not change these ids. Otherwise, all the tests developed will crash. For the moment, all my tests regarding this part are working. – sunlover3 Dec 08 '17 at 09:41

14 Answers14

88

To not depend on the app locale, you can use the code from Matt Logan by replacing "Navigate up" with R.string.abc_action_bar_up_description:

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

This helped me a lot because I have an app in more than 5 languages and I had to act like this.

dsh
  • 12,037
  • 3
  • 33
  • 51
sunlover3
  • 1,999
  • 1
  • 20
  • 25
  • 4
    This should be the accepted answer. The accepted one only works on English devices... #fail – Roc Boronat Jun 01 '16 at 10:28
  • 1
    Thanks. I always think of using general commands, so this is why I searched for the android native string id. Good luck! – sunlover3 Jun 29 '16 at 07:12
  • I would like to help you. Can you tell me how did you receive this error? You must be careful that before calling onView on any view, be sure that nothing overlaps it (like a dialog or some other screen). My code works pretty fine, you have a mistake in your code structure. – sunlover3 Jun 29 '16 at 21:44
  • I have the same problem as @AutonomousApps. how can I check to see of anything is overlapping? Shouldn't the withContentDescription bring the view to the top level view that contains the Navigate up button? – Frikster Dec 01 '16 at 03:43
  • 3
    I found solution without workaround. Here: val upButton = onView(allOf(instanceOf(ImageButton::class.java), withParent(withId(toolBar)))) – Alexei Jun 06 '19 at 08:03
  • 2
    for people using androidx, it's `androidx.appcompat.R.string.abc_action_bar_up_description` – Clément Jean Feb 05 '21 at 08:39
  • with navigation component, this is only working in English, when I try in French it fails. @AthaMit's answer works for both languages – Oya Canli Mar 27 '21 at 13:18
  • @OyaCanli I'm sure that since 2016 the things might have changed. Thanks for the update! – sunlover3 Mar 27 '21 at 20:42
33

Use the withContentDescription() Matcher:

onView(withContentDescription("Navigate up")).perform(click());
Matt Logan
  • 5,886
  • 5
  • 31
  • 48
  • 13
    This works, however, this may fail on devices that are not set to English – nickmartens1980 Dec 08 '14 at 12:55
  • 1
    If you look for this log in your failures in your language: `+------->ImageButton{id=-1, desc=Navigate up,`, the `desc` value will be the translated description in the device's current language. I'd imagine that's buried somewhere within the `android.R.string` declarations, but I haven't been able to ferret out where it's hidden. – DesignatedNerd Jan 21 '15 at 06:06
  • Not sure why the different language thing is a big deal, unless you're running your automated tests on devices set to a different language. Even in this case, you could just `switch` on the device's default `Locale` to set the appropriate "Navigate up" translation. – Matt Logan Jan 21 '15 at 06:38
  • 6
    Use with caution, because: _1)_ apparently in **4.3** content description is **", Navigate up"** and matching will fail. _2)_ You can have more than one view with a such description. In particular, only `onView(allOf(withContentDescription(containsString("Navigate up")), isClickable())).perform(click())` works for me. – Ghedeon Feb 19 '15 at 22:20
21

I had trouble navigating back from one Activity to another, but then I found top-level actions:

Espresso.pressBack();
  • 23
    Please notice that back navigation is not the same as up navigation: http://developer.android.com/design/patterns/navigation.html – bryant1410 May 13 '16 at 15:58
14

I found a real solution to this issue. By using the hierarchyviewer I found that the toolbar looks like this: hierarchyviewer screenshot

This means we could match the hamburger icon (not back button) like this:

onView(withContentDescription("Open navigation")).perform(click());

But a better solution to me was to find out that the hamburger icon is the only ImageButton and a direct child view of the v7 Toolbar. So I wrote a helper method to match it:

public static Matcher<View> androidHomeMatcher() {
    return allOf(
        withParent(withClassName(is(Toolbar.class.getName()))),
        withClassName(anyOf(
            is(ImageButton.class.getName()),
            is(AppCompatImageButton.class.getName())
    )));
}

@Test
public void clickHamburgerIcon() throws Exception {
    onView(androidHomeMatcher()).perform(click());
    // ...
}

This solution is better because it should match the view no matter which locale you use in your test. :-)

EDIT: Note that Toolbar might be android.support.v7.widget.Toolbar or android.widget.Toolbar - depending on your use case!

EDIT: The support lib version 24.2.0 uses AppCompatImageButton instead of ImageButton, so I added it, too.

EDIT: You have to import the correct methods to get this to work. Here are the imports used:

import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
import static android.support.test.espresso.matcher.ViewMatchers.withParent;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
mreichelt
  • 12,359
  • 6
  • 56
  • 70
5
Espresso.pressBack();

Or

onView(withContentDescription("Navigate up")).perform(click());
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Dong Thang
  • 410
  • 6
  • 6
5

I was having problems with "Navigate up" in an emulator, this worked for me:

onView(isRoot()).perform(ViewActions.pressMenuKey());
Robin S
  • 199
  • 1
  • 4
  • 9
5
public static Matcher<View> navigationIconMatcher() {
    return allOf(
            isAssignableFrom(ImageButton.class),
            withParent(isAssignableFrom(Toolbar.class)));
}

@Test
public void clickHamburgerIcon() throws Exception {
onView(navigationIconMatcher()).perform(click());
// ...
}

this works always!

user2210676
  • 51
  • 1
  • 3
  • yes, it works, but only with `Toolbar` widget. For system navigation you should choose another solution – Beloo Sep 16 '19 at 11:21
3

If your intention is opening / closing the drawer, I recommend using the Espresso contrib library:

onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
Miloš Černilovský
  • 3,846
  • 1
  • 28
  • 30
2

To press back View:

onView(isRoot()).perform(pressBack());
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Testing Singh
  • 1,347
  • 1
  • 15
  • 26
1
//click on the navigation up button to go back to the list
onView(withContentDescription(getToolbarNavigationContentDescription())).perform(click());

Methods:

private String getToolbarNavigationContentDescription() {
    return TestUtils.getToolbarNavigationContentDescription(
            activityTestRule.getActivity(), R.id.toolbar);
}

public static String getToolbarNavigationContentDescription(
        @NonNull Activity activity, @IdRes int toolbarId) {
    Toolbar toolbar = activity.findViewById(toolbarId);
    if (toolbar != null) {
        return (String) toolbar.getNavigationContentDescription();
    } else {
        throw new RuntimeException("No toolbar found.");
    }
}
AppiDevo
  • 3,195
  • 3
  • 33
  • 51
1

Update: I couldnt find R.string.abc_action_bar_up_description, maybe it has to do with androidx. I'm not sure about this. Instead I used R.string.nav_app_bar_navigate_up_description The code looks like this:

onView(withContentDescription(R.string.nav_app_bar_navigate_up_description)).perform(click())
AthaMit
  • 310
  • 1
  • 4
  • 10
  • 1
    Thanks! The accepted anwser only works in English for me since I migrated to Jetpack navigation. This solution worked in French as well. – Oya Canli Mar 27 '21 at 13:19
0

onView(withContentDescription("Open navigation drawer")).perform(click())

this helped me

  • 2
    Hello! While this code may solve the question, [including an explanation](https://meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. – Brian61354270 Apr 21 '20 at 00:38
-2

Maybe you can call:

pressKey(KeyEvent.KEYCODE_HOME);
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
-3

Add onbackpress in your activity, and use:

onView(withContentDescription("Navigate up")).perform(click());
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
Dhiraj Himani
  • 1,062
  • 12
  • 9