androidandroid-espressomatcherandroid-espresso-recorder

How to access a element by class name within a parent element with R.id


I'm writing an automated test for logging in to an Android app. I'm recording tests with Record Espresso Test and then edit the code as it's usually full of bugs. I'm using espresso androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2', and uiAutomatorViewer to double check the R.id's~ and class names.

I've encountered a problem while trying to edit the text in the element with no R.id but with class name android.widget.EditText:

android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: (with id: com.mydosesmart:id/til_name and an instance of android.widget.FrameLayout and an instance of android.widget.EditText)

Problem is that the element with class name android.widget.EditText has no R.id.. This class name is not unique for this view, this element with class name android.widget.EditText has a parent element with a unique R.id..

On the login view in the app, two elements have the class name android.widget.EditText therefore I can't call this element just by class name. I want to call it like that: within the element with R.id.til_name find the element with the class name android.widget.EditText. Below is the code I'm using now and it fails.

ViewInteraction textInputEditText2 = onView(
                allOf(withId(R.id.til_name), instanceOf(Class.forName("android.widget.FrameLayout")), instanceOf(Class.forName("android.widget.EditText"))));
        textInputEditText2.perform(replaceText("testespresso"), closeSoftKeyboard());

That fails too:

ViewInteraction textInputEditText2 = onView(
                allOf(withId(R.id.til_name), instanceOf(Class.forName("android.widget.EditText"))));
        textInputEditText2.perform(replaceText("testespresso"), closeSoftKeyboard());

As plenty of elements in the app I'm testing have no designated R.id I would like to find an easy way to call them for testing purposes.


Solution

  • After trying dozens of different matchers in all possible combinations I've found the answer to my question. So far it seems to be universal: onView(allOf(withClassName(containsString(EditText.class.getSimpleName())), isDescendantOfA(withId(R.id.til_name)))) .perform(replaceText("testespresso "), closeSoftKeyboard());

    Using isDescendantOfA we don't need to worry if the element we are looking for has a parent/grandparent with R.id, it just needs to be lower in a hierarchy.