How to select a specific Tab position in Tab Layout using Espresso testing

Both of the above answers are good. For me, after selecting the tab I also wanted to verify that it is the correct tab by matching with the title. Since, this post is about espresso testing, I will share the code snippet here as it may be helpful to someone.

fun matchCurrentTabTitle(tabTitle: String): Matcher<View> {
return object : TypeSafeMatcher<View>() {
    override fun describeTo(description: Description?) {
        description?.appendText("unable to match title of current selected tab with $tabTitle")
    }

    override fun matchesSafely(item: View?): Boolean {
        val tabLayout = item as TabLayout
        val tabAtIndex: TabLayout.Tab = tabLayout.getTabAt(tabLayout.selectedTabPosition)
                ?: throw PerformException.Builder()
                        .withCause(Throwable("No tab at index ${tabLayout.selectedTabPosition}"))
                        .build()

        return tabAtIndex.text.toString().contains(tabTitle, true)
    }
  }
}

fun matchTabTitleAtPosition(tabTitle: String, tabIndex: Int): Matcher<View> {
return object : TypeSafeMatcher<View>() {
    override fun describeTo(description: Description?) {
        description?.appendText("unable to select tab at index $tabIndex and match title with $tabTitle")
    }

    override fun matchesSafely(item: View?): Boolean {
        val tabLayout = item as TabLayout
        val tabAtIndex: TabLayout.Tab = tabLayout.getTabAt(tabIndex)
                ?: throw PerformException.Builder()
                        .withCause(Throwable("No tab at index $tabIndex"))
                        .build()

        return tabAtIndex.text.toString().contains(tabTitle, true)
    }
  }
}

As someone who hasn't switched/used Kotlin, here is it in Java. It's basically the same as @Be_Negative so just use it in the same way.

@NonNull
private static ViewAction selectTabAtPosition(final int position) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return allOf(isDisplayed(), isAssignableFrom(TabLayout.class));
        }

        @Override
        public String getDescription() {
            return "with tab at index" + String.valueOf(position);
        }

        @Override
        public void perform(UiController uiController, View view) {
            if (view instanceof TabLayout) {
                TabLayout tabLayout = (TabLayout) view;
                TabLayout.Tab tab = tabLayout.getTabAt(position);

                if (tab != null) {
                    tab.select();
                }
            }
        }
    };
}

Should be doable with a custom ViewAction. Something like this:

fun selectTabAtPosition(tabIndex: Int): ViewAction {
    return object : ViewAction {
        override fun getDescription() = "with tab at index $tabIndex"

        override fun getConstraints() = allOf(isDisplayed(), isAssignableFrom(TabLayout::class.java))

        override fun perform(uiController: UiController, view: View) {
            val tabLayout = view as TabLayout
            val tabAtIndex: TabLayout.Tab = tabLayout.getTabAt(tabIndex)
                    ?: throw PerformException.Builder()
                            .withCause(Throwable("No tab at index $tabIndex"))
                            .build()

            tabAtIndex.select()
        }
    }
}

and the usage:

onView(withId(R.id.tab_layout)).perform(selectTabAtPosition(99))