Use espresso with custom EditText

Viewed 1418

Here is part of my layout:

<com.rey.material.widget.EditText
            android:id="@+id/tagEditorSettings"
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:layout_marginStart="10dp"
            android:layout_toEndOf="@+id/circularProgressbar"
            android:hint="@string/tag_name_tag_settings"
            android:inputType="text"
            android:textSize="18sp"
            app:et_dividerColor="@color/my_primary"
            app:et_dividerHeight="1dp"
            app:et_inputId="@+id/name_input"
            app:et_labelEnable="true"
            app:et_labelTextSize="14sp"
            app:et_supportLines="1" />

and my testing code:

onView(withId(R.id.tagEditorSettings))
                .perform(click()).perform(clearText());

And when I try to run it I get such error:

Caused by: java.lang.RuntimeException: Action will not be performed because the target view does not match one or more of the following constraints: (is displayed on the screen to the user and is assignable from class: class android.widget.EditText)

As I understand the problem is with "is assignable from class: class android.widget.EditText", but could someone please advice, how I can fix it and use in my case?

3 Answers

Issue: com.rey.material.widget.EditText is extended from Framelayout and clearText uses ReplaceTextAction as

  public static ViewAction clearText() {
      return actionWithAssertions(new ReplaceTextAction(""));
  }

where ReplaceTextAction enforces the view, a type of EditText as

allOf(isDisplayed(), isAssignableFrom(EditText.class));
//                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Since rey...EdiText is not a subclass of EditText hence the error


Solution : create your own ViewAction as

public static ViewAction clearTextInCustomView(){
            return new ViewAction() {
                @SuppressWarnings("unchecked")
                @Override
                public Matcher<View> getConstraints() {
                    return allOf(isDisplayed(), isAssignableFrom(com.rey.material.widget.EditText.class));
//                                            ^^^^^^^^^^^^^^^^^^^ 
// To check that the found view is type of com.rey.material.widget.EditText or it's subclass
                }

                @Override
                public void perform(UiController uiController, View view) {
                    ((com.rey.material.widget.EditText) view).setText("");
                }

                @Override
                public String getDescription() {
                    return "clear text";
                }
            };
    }

And later you can do

onView(withId(R.id.tagEditorSettings))
                .perform(click()).perform(clearTextInCustomView());

this custom EditText extends from FrameLayout. That's why it's complaining that the target is not assignable from class android.widget.EditText.

But, this custom EditText has some methods like setText to set the text in the contained EditText which is defined as:

protected android.widget.EditText mInputView;

and this one is an EditText from the android.widget package.

You can crate a custom ViewAction to access this public methods:

public static ViewAction setTextEditText(final Matcher<View> matcher,
   final String newText) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return allOf(isDisplayed(), isAssignableFrom(com.rey.material.widget.EditText.class));
        }

        @Override
        public String getDescription() {
            return "Update the text from the custom EditText";
        }

        @Override
        public void perform(final UiController uiController, final View view) {
            ((com.rey.material.widget.EditText) view).setText(newText);
        }
    };
}

Then, you can execute the custom ViewAction:

onView(withId(R.id.tagEditorSettings)).setTextEditText(null);

For kotlin solutioning:

fun setTextEditText(
    newText: String?
): ViewAction {
    return object : ViewAction {

        override fun getConstraints(): Matcher<View> {
            return CoreMatchers.allOf(
                ViewMatchers.isDisplayed(),
                ViewMatchers.isAssignableFrom(CustomDefaultInput::class.java)
            )
        }

        override fun getDescription(): String {
            return "Update the text from the custom EditText"
        }

        override fun perform(uiController: UiController?, view: View) {
            (view as CustomDefaultInput).textInputEditText.setText(newText)
        }
    }
}

Then you run with :

onView(withId(R.id.inputCityName)).perform(setTextEditText(newText = "Ab"))
Related