How open tab in Espresso

2019-03-21 02:18发布

问题:

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

回答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



回答2:

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.



回答3:

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


回答4:

onView(withText("Your tab label")).perform(click())