Search code examples
androidandroid-layoutandroid-testinghamcrestandroid-espresso

How to match this table cell using Espresso?


I need to match the view which is highlighted by a red rectangle. Which Espresso expression should I write for it?

enter image description here

This is a table layout, all cells are instances of TextView. There are no unique IDs for cell views. The view of interest may or may not have text inside. All I know is that this view is always sitting below the "Food Group" cell.

Any clues would be welcome.


Solution

  • Here is the test I would write in your case.

    public void testCellBelowFoodGroup() {
        getActivity();
        onView(
            allOf(
                isDescendantOfA(isAssignableFrom(TableLayout.class)),
                isInRowBelow(withText("Food Group")),
                hasChildPosition(0)
                )
        ).check(matches(withText("TEXT TO BE FOUND")));
    }
    

    So, we are looking for a view inside a given TableLayout, that is in a row below the "Food Group" text, and that is the leftmost element of the row. Then we can do whatever we want with that view, e.g. check its text.

    isInRowBelow and hasChildPosition are not provided by Espresso, they are custom methods, as is usual when testing with Espresso: you are encouraged to create your own view assertions and view matchers.

    Here is the implementation.

    static Matcher<View> isInRowBelow(final Matcher<View> viewInRowAbove) {
        checkNotNull(viewInRowAbove);
        return new TypeSafeMatcher<View>(){
    
            @Override
            public void describeTo(Description description) {
                description.appendText("is below a: ");
                viewInRowAbove.describeTo(description);
            }
    
            @Override
            public boolean matchesSafely(View view) {
                // Find the current row
                ViewParent viewParent = view.getParent();
                if (!(viewParent instanceof TableRow)) {
                    return false;
                }
                TableRow currentRow = (TableRow) viewParent;
                // Find the row above
                TableLayout table = (TableLayout) currentRow.getParent();
                int currentRowIndex = table.indexOfChild(currentRow);
                if (currentRowIndex < 1) {
                    return false;
                }
                TableRow rowAbove = (TableRow) table.getChildAt(currentRowIndex - 1);
                // Does the row above contains at least one view that matches viewInRowAbove?
                for(int i = 0 ; i < rowAbove.getChildCount() ; i++) {
                    if (viewInRowAbove.matches(rowAbove.getChildAt(i))) {
                        return true;
                    }
                }
                return false;
            }};
    }
    
    static Matcher<View> hasChildPosition(final int i) {
        return new TypeSafeMatcher<View>(){
    
            @Override
            public void describeTo(Description description) {
                description.appendText("is child #" + i);
            }
    
            @Override
            public boolean matchesSafely(View view) {
                ViewParent viewParent = view.getParent();
                if (!(viewParent instanceof ViewGroup)) {
                    return false;
                }
                ViewGroup viewGroup = (ViewGroup) viewParent;
                return (viewGroup.indexOfChild(view) == i);
            }};
    }
    

    The full source code can be downloaded from https://github.com/acontal/android-stackoverflow-espresso-match_table_cell