How can i open tab in Espresso test? I tried to do Espresso.onView(ViewMatchers.withId(R.id.practice_results_tab)).perform(ViewActions.click());
, but that doesn't working. In that code i opened Layout of this tab.
There is the XML file:
<TabHost
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/practice_tabHost">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</TabWidget>
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:id="@+id/practice_settings_tab"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
</LinearLayout>
<LinearLayout
android:id="@+id/practice_results_tab"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
</LinearLayout>
</FrameLayout>
</LinearLayout>
</TabHost>
What ID should I use to open tab?
Error in logcat:
Caused by: java.lang.RuntimeException: Action will not be performed because the target view does not match one or more of the following constraints:
at least 90 percent of the view's area is displayed to the user.
Target view: "LinearLayout{id=2131296384, res-name=practice_results_tab, visibility=GONE, width=0, height=0, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=true, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}"
Completed Code
You really need to add code for us to give a proper answer. I'm guessing here that you used a TabHost and TabWidget in the way this example does: https://maxalley.wordpress.com/2012/10/25/android-creating-a-tab-layout-with-tabhost-and-tabwidget/
I've created a sample project at https://github.com/hanscappelle/SO-25016397
Your Activity could then look like this:
public class MainActivity extends TabActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TabHost tabHost = getTabHost();
// setNewTab(context, tabHost, tag, title, icon, contentID);
this.setNewTab(this, tabHost, "tab1", R.string.textTabTitle1, R.drawable.ic_tab_settings, R.id.practice_settings_tab);
this.setNewTab(this, tabHost, "tab2", R.string.textTabTitle2, R.drawable.ic_tab_results, R.id.practice_results_tab);
}
private void setNewTab(Context context, TabHost tabHost, String tag, int title, int icon, int contentID ){
TabSpec tabSpec = tabHost.newTabSpec(tag);
String titleString = getString(title);
tabSpec.setIndicator(titleString, context.getResources().getDrawable(android.R.drawable.star_on));
tabSpec.setContent(contentID);
tabHost.addTab(tabSpec);
}
}
I found another code example at https://maxalley.wordpress.com/2012/10/27/android-styling-the-tabs-in-a-tabwidget/ with a helper method that injects an ImageView
for the tabs.
private View getTabIndicator(Context context, int title, int icon) {
View view = LayoutInflater.from(context).inflate(R.layout.tab_layout, null);
ImageView iv = (ImageView) view.findViewById(R.id.imageView);
iv.setImageResource(icon);
TextView tv = (TextView) view.findViewById(R.id.textView);
tv.setText(title);
return view;
}
Now it gets interesting cause this way we can easily set an ID or a tag on these injected View
objects and use these in Espresso.
A solution: Tag the view
If you adapt that helper to accept a tag for each view helper code will look something like this:
private View getTabIndicator(Context context, int title, int icon, int viewId, String viewTag) {
View view = LayoutInflater.from(context).inflate(R.layout.tab_layout, null);
ImageView iv = (ImageView) view.findViewById(R.id.image_view);
iv.setImageResource(icon);
TextView tv = (TextView) view.findViewById(R.id.text_view);
tv.setText(title);
tv.setTag(viewTag);
return view;
}
If you use only icons you can set the ID on the ImageView also.
And the Espresso code to click these tabs:
Espresso.onView(ViewMatchers.withTagValue(Matchers.is((Object)"tab1"))).perform(ViewActions.click());
Alternative Solution: using View IDs
If you go for IDs you need these IDs to be defined somewhere. Use a simple Android resource file with some ID definitions.
The view ID resource file, named /values/ids.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="tab1" type="id" />
</resources>
The adapted helper:
private View getTabIndicator(Context context, int title, int icon, int viewId, String viewTag) {
View view = LayoutInflater.from(context).inflate(R.layout.tab_layout, null);
ImageView iv = (ImageView) view.findViewById(R.id.image_view);
iv.setImageResource(icon);
TextView tv = (TextView) view.findViewById(R.id.text_view);
tv.setText(title);
tv.setId(viewId);
return view;
}
If you use only icons you can set the ID on the ImageView also.
And the Espresso code to click these tabs:
Espresso.onView(ViewMatchers.withId(R.id.tab1)).perform(ViewActions.click());
About TabHosts in general
Why did you use this TabHost in the first place? Note that this class is deprecated by now. A ViewPager with tabs or the ActionBar might be better options depending on your use case.
Use the ViewHierarchy tool
In cases like this the first problem is often to find the proper view. For this use the View Hierarchy tool. It's part of the android SDK and lives in the tools directory.
You can start it from command line like this:
cd ANDROID_SDK_LOCATION/tools
hierarchyviewer
Or use Android Studio menu: Tools > Android > Android Device Monitor. Then open the Hierarchy View perspective from the menu in the device monitor: Window > Open Perspective > Hierarchy View .
I prefer the first option just because the Device Monitor does way too much for our intentions.
Now use the Layout View in combination with the View Properties view to find the ID and tags of the view you want.
Some instructions on this tool: http://developer.android.com/tools/debugging/debugging-ui.html
the only thing that works for me is:
public void clickOnTab(String tabText) {
Matcher<View> matcher = allOf(withText(tabText),
isDescendantOfA(withId(R.id.ll_tab_container)));
onView(matcher).perform(click());
}
where ll_tab_container
is the linearlayout for my custom tab layout. and if you had a tab called "shopping" then you would pass that as the tabText.
It is also possible to set a tag on your tab text without using a custom indicator method. You do this by accessing the tab's TextView
after it has been created.
Using the MainActivity
from hcpl's example code, that would look something like this:
public MainActivity extends TabActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TabHost tabHost = getTabHost();
// setNewTab(context, tabHost, tag, title, icon, contentID);
this.setNewTab(this, tabHost, "tab1", R.string.textTabTitle1, R.drawable.ic_tab_settings, R.id.practice_settings_tab);
this.setNewTab(this, tabHost, "tab2", R.string.textTabTitle2, R.drawable.ic_tab_results, R.id.practice_results_tab);
// Set custom tag on first tab
View tabView1 = tabHost.getTabWidget().getChildAt(0);
TextView tabView1Text = (TextView) signUpTabView.findViewById(android.R.id.title);
signUpTextTab.setTag("TAG_TAB_ONE");
// Set custom tag on second tab
View tabView2 = tabHost.getTabWidget().getChildAt(1);
TextView tabView1Text = (TextView) signUpTabView.findViewById(android.R.id.title);
signUpTextTab.setTag("TAG_TAB_TWO");
}
}
Then in your Espresso test, you could access the tabs like this:
onView(withTagValue(Matchers.is((Object)"TAG_TAB_TWO"))).perform(click());
onView(withText("Your tab label")).perform(click())