Как получить доступ к editText в MaterialDialog и ввести текст (тест пользовательского интерфейса Android)

введите описание изображения здесь

Это мой код

@RunWith(AndroidJUnit4.class)
@LargeTest
public class ChangeTextBehaviorTest {

public static final String STRING_TO_BE_TYPED = "Espresso";

@Rule
public ActivityTestRule<LandingActivity> mActivityRule = new ActivityTestRule<> (
        LandingActivity.class );

@Test
public
void changeText_sameActivity () {
    String testText = "testText";
    onView ( withId ( R.id.action_a) )
            .perform ( click (), closeSoftKeyboard () );
    onView ( withHint ( R.string.add_list ) ).perform ( typeTextIntoFocusedView ( testText ) );
    onView ( withText ("ADD" ) ).perform ( click () );

}

Сначала мне нужно нажать на floatButton, чтобы открыть MaterialDialog.

Затем я пытаюсь получить доступ к ссылке editText по ее подсказке

onView ( withHint ( R.string.add_list ) ).perform ( typeTextIntoFocusedView ( testText ) );

Но тест дал ошибку. Кажется, что диалог не находится в иерархии представлений.

    Running tests
Test running started
android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: with string from resource id: <2131099668>[add_list] value: Add List
If the target view is not part of the view hierarchy, you may need to use Espresso.onData to load it from one of the following AdapterViews:com.nhaarman.listviewanimations.itemmanipulation.DynamicListView{42ed6100 V.ED.VCL ........ 0,0-1080,1692 #7f0c0074 app:id/listViewTaskInComplete}

View Hierarchy:
+>DecorView{id=-1, visibility=VISIBLE, width=1080, height=1920, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}
|
+->LinearLayout{id=-1, visibility=VISIBLE, width=1080, height=1920, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=2}
|
+-->ViewStub{id=16909084, 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}
|
+-->FrameLayout{id=-1, visibility=VISIBLE, width=1080, height=1860, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=60.0, child-count=1}
|
+--->FitWindowsLinearLayout{id=2131492948, res-name=action_bar_root, visibility=VISIBLE, width=1080, height=1860, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=2}
|
+---->ViewStubCompat{id=2131492949, res-name=action_mode_bar_stub, 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}
|
+---->ContentFrameLayout{id=16908290, res-name=content, visibility=VISIBLE, width=1080, height=1860, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}
|
+----->CoordinatorLayout{id=2131492976, res-name=rootLayout, visibility=VISIBLE, width=1080, height=1860, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=3}
|
+------>AppBarLayout{id=-1, visibility=VISIBLE, width=1080, height=540, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1}
|
+------->CollapsingToolbarLayout{id=2131492977, res-name=collapsingToolbarLayout, visibility=VISIBLE, width=1080, height=540, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=2}
|
+-------->ImageView{id=-1, visibility=VISIBLE, width=1080, height=540, 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=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0}
|
+-------->Toolbar{id=2131492978, res-name=toolbar, visibility=VISIBLE, width=1080, height=168, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=2}
|
+--------->View{id=-1, visibility=VISIBLE, width=888, height=168, 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=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=48.0, y=0.0}
|
+--------->ActionMenuView{id=-1, visibility=VISIBLE, width=144, height=168, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=936.0, y=0.0, child-count=1}
|
+---------->ActionMenuItemView{id=2131493079, res-name=menu_setting, desc=Settings, visibility=VISIBLE, width=144, height=144, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=12.0, text=, input-type=0, ime-target=false, has-links=false}
|
+------>NestedScrollView{id=2131492979, res-name=nested_scroll_view, visibility=VISIBLE, width=1080, height=1860, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=540.0, child-count=1}
|
+------->DynamicListView{id=2131492980, res-name=listViewTaskInComplete, visibility=VISIBLE, width=1080, height=1692, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=0}
|
+------>FloatingActionsMenu{id=2131493035, res-name=multiple_actions, visibility=VISIBLE, width=222, height=828, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=429.0, y=1032.0, child-count=3}
|
+------->FloatingActionButton{id=2131493036, res-name=action_b, visibility=VISIBLE, width=222, height=222, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=138.0}
|
+------->FloatingActionButton{id=2131493037, res-name=action_a, visibility=VISIBLE, width=222, height=222, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=372.0}
|
+------->{id=2131492868, res-name=fab_expand_menu_button, visibility=VISIBLE, width=222, height=222, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=606.0}
|

Это мой код для создания диалога из этой библиотеки https://github.com/afollestad/material-dialogs

    public static
void showAddListDialog ( final Activity thisContext, final ListView listView ) {
    MaterialDialog scoreDialog = new MaterialDialog.Builder ( thisContext )
            //.customView ( R.layout.dialog_todo, true )
            .title ( thisContext.getString ( R.string.add_list ) )
            .positiveText ( "ADD" )
            .input ( thisContext.getString ( R.string.add_list ), "", new MaterialDialog.InputCallback () {
                @Override public
                void onInput ( MaterialDialog materialDialog, CharSequence charSequence ) {
                    QueryHelper.addListToDB ( thisContext, String.valueOf ( charSequence ), listView );
                }
            } )
            .negativeText ( "CANCEL" )
            .show ();
}

person UmAnusorn    schedule 30.09.2015    source источник


Ответы (1)


Я могу придумать 2 возможных причины:

1) Возможно, диалог все еще находится в переходном состоянии, и Espresso не считает его полностью видимым. Или еще не прикреплен.

Иногда Espresso терпит неудачу - даже если он обещает подождать, пока основной поток не будет простаивать, иногда это не срабатывает.

Когда мне нужно выполнить действия в диалогах, я использую несколько вспомогательных методов, чтобы немного подождать переходов и еще чего-то, чтобы закончить. (код ниже)

2) Espresso ищет представление в неправильном корневом представлении.


Нажмите на просмотр в диалоговом окне «Материал» (используя ту же библиотеку, что и вы)

public void test() {
    // This opens time picker dialog
    onView(withId(R.id.block_edit_start_time)).perform(click());

    // Click on dialog OK button
   performMaterialDialogOkClick();
}

/**
 * Clicks positive button in visible/active {@link com.afollestad.materialdialogs.MaterialDialog}
 */
public static void performMaterialDialogOkClick() {
    onView(withId(R.id.buttonDefaultPositive)).inRoot(isDialog()).perform(click());
}

Ожидание завершения переходов

public void test() {
    // Open another screen
    goTo(new XyzScreen(DateTime.now(), null));

    // Wait some time for transitions to complete
    onView(isRoot()).perform(waitAtLeast(300));

    // Now we can get a screenshot
    screenshot(d);
}

/**
 * Perform action of waiting for a specific time. Useful when you need
 * to wait for animations to end and Espresso fails at waiting.
 * <p/>
 * E.g.:
 * onView(isRoot()).perform(waitAtLeast(Sampling.SECONDS_15));
 *
 * @param millis
 * @return
 */
public static ViewAction waitAtLeast(final long millis) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return anything();
        }

        @Override
        public String getDescription() {
            return "wait for at least " + millis + " millis.";
        }

        @Override
        public void perform(final UiController uiController, final View view) {
            uiController.loopMainThreadUntilIdle();
            uiController.loopMainThreadForAtLeast(millis);
        }
    };
}

/**
 * Perform action of waiting until UI thread is free.
 * <p/>
 * E.g.:
 * onView(isRoot()).perform(waitUntilIdle());
 *
 * @return
 */
public static ViewAction waitUntilIdle() {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return anything();
        }

        @Override
        public String getDescription() {
            return "wait until UI thread is free";
        }

        @Override
        public void perform(final UiController uiController, final View view) {
            uiController.loopMainThreadUntilIdle();
        }
    };
}
person mindeh    schedule 30.09.2015
comment
Я получил ошибку return something (); общедоступный статический ViewAction waitUntilIdle () {вернуть новое ViewAction () {aOverride общедоступный Matcher ‹View› getConstraints () {вернуть что-нибудь (); } aOverride public String getDescription () {return wait, пока поток пользовательского интерфейса не освободится; } aOverride public void perform (финальный UiController uiController, финальный просмотр) {uiController.loopMainThreadUntilIdle (); }}; } - person UmAnusorn; 20.10.2015
comment
Ошибка: (122, 19) ошибка: требуются несовместимые типы: Matcher ‹View› найдено: Matcher ‹Object› - person UmAnusorn; 20.10.2015
comment
О, это странно, возможно, что-то изменилось с тех пор, как я написал этот код. Они это вместо anything() сопоставителя: `public static Matcher ‹View› anyView () {return new TypeSafeMatcher ‹View› () {@Override public boolean matchesSafely (Просмотр представления) {return true; } @Override public void describeTo (Описание description) {}}; } ` - person mindeh; 26.10.2015
comment
В этом сообщении были исправлены мои проблемы stackoverflow.com/questions/33255294/ - person UmAnusorn; 26.10.2015