Espresso不会等待ViewPager上的滑动操作完成

时间:2016-05-18 08:27:58

标签: android android-espresso

宣传Espresso的功能是它始终等待Android的UI-Thread闲置,这样您就不必处理任何时间问题。但我似乎找到了一个例外: - /

设置为ViewPager,每个片段中都有EditText。我希望Espresso在第一个片段的EditText中键入文本,然后滑动到第二个片段并对该片段中的EditText执行相同的操作(3次):

@MediumTest
public void testSwipe() throws InterruptedException {
    onView(withIdInActiveFragment(EXTERN_HOURS_INPUT))
            .perform(typeText("8.0"));
    onView(withIdInActiveFragment(DAY_PAGER))
            .perform(swipeLeft());
    //Thread.sleep(2000); // <--- uncomment this and the test runs fine
    onView(withIdInActiveFragment(EXTERN_HOURS_INPUT))
            .perform(typeText("8.0"));
    onView(withIdInActiveFragment(DAY_PAGER))
            .perform(swipeLeft());
    //Thread.sleep(2000);
    onView(withIdInActiveFragment(EXTERN_HOURS_INPUT))
            .perform(typeText("8.0"));
    onView(withIdInActiveFragment(DAY_PAGER))
            .perform(swipeLeft());
}

public static Matcher<View> withIdInActiveFragment(int id) {
    return Matchers.allOf(withParent(isDisplayed()), withId(id));
}

但是在执行第一次滑动时出现此错误:

android.support.test.espresso.AmbiguousViewMatcherException: '(has parent matching: is displayed on the screen to the user and with id: de.cp.cp_app_android:id/extern_hours_input)' matches multiple views in the hierarchy.
Problem views are marked with '****MATCHES****' below.

View Hierarchy:
...

+-------->AppCompatEditText{id=2131558508, res-name=extern_hours_input, visibility=VISIBLE, width=110, height=91, has-focus=true, has-focusable=true, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=true, is-focusable=true, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=true, editor-info=[inputType=0x2002 imeOptions=0x6 privateImeOptions=null actionLabel=null actionId=0 initialSelStart=3 initialSelEnd=3 initialCapsMode=0x0 hintText=null label=null packageName=null fieldId=0 fieldName=null extras=null ], x=165.0, y=172.0, text=8.0, input-type=8194, ime-target=true, has-links=false} ****MATCHES****

...  


+-------->AppCompatEditText{id=2131558508, res-name=extern_hours_input, visibility=VISIBLE, width=110, height=91, 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=true, editor-info=[inputType=0x2002 imeOptions=0x6 privateImeOptions=null actionLabel=null actionId=0 initialSelStart=0 initialSelEnd=0 initialCapsMode=0x2000 hintText=null label=null packageName=null fieldId=0 fieldName=null extras=null ], x=165.0, y=172.0, text=, input-type=8194, ime-target=false, has-links=false} ****MATCHES****

Espresso希望写入EditText,其中显示的ID为EXTERN_HOURS_INPUT。由于滑动操作尚未完成,因此第一个和第二个片段中的EditTexts都可见,这就是为什么匹配的onView(withIdInActiveFragment(EXTERN_HOURS_INPUT))失败并且匹配为2个。

如果我在滑动操作后添加Thread.sleep(2000);手动强制休息,一切都很好。

有人知道如何使Espresso等到滑动操作完成后?或者至少有人知道,为什么会这样?因为在执行滑动操作时UI-Thread不能处于空闲状态,可以吗?

以下是activity_day_time_record.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">

<data>
    <variable
        name="timerecord"
        type="de.cp.cp_app_android.model.TimerecordDatabindingWrapper" />
</data>

<ScrollView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        style="@style/cp_relative_layout"
        android:descendantFocusability="afterDescendants"
        tools:context=".activities.DayRecordActivity">

        <include
            android:id="@+id/toolbar"
            layout="@layout/cp_toolbar"></include>


        <!-- Arbeitsstunden -->
        <TextView xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/section_title_workhours"
            style="?android:attr/listSeparatorTextViewStyle"
            android:layout_width="match_parent"
            android:layout_height="25dip"
            android:layout_below="@id/toolbar"
            android:text="@string/dayrecord_section_workhours" />

        <TextView
            android:id="@+id/extern_hours"
            style="@style/dayrecord_label"
            android:layout_below="@id/section_title_workhours"
            android:text="@string/dayrecord_label_extern_hours" />

        <EditText
            android:id="@+id/extern_hours_input"
            style="@style/dayrecord_decimal_input"
            android:layout_alignBaseline="@id/extern_hours"
            android:layout_toEndOf="@id/extern_hours"
            android:layout_toRightOf="@id/extern_hours"
            bind:addTextChangedListener="@{timerecord.changed}"
            bind:binding="@{timerecord.hoursExtern}"
            bind:setOnFocusChangeListener="@{timerecord.hoursExternChanged}" />
        <!--    android:text='@{timerecord.hoursExtern != null ? String.format("%.1f", timerecord.hoursExtern) : ""}' -->


    </RelativeLayout>
</ScrollView>

activity_swipe_day.xml

<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/day_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" >

1 个答案:

答案 0 :(得分:3)

我不认为Espresso内置了此功能,他们希望您使用idling-resource。 我提出的最好的仍然是使用睡眠,但每100毫秒轮询一次,所以最糟糕的情况是在视图变得可见后返回100毫秒。

using (var s = sf.OpenSession())
using (var tx = s.BeginTransaction())
{
    var blogs = s.CreateCriteria<Blog>()
        .SetMaxResults(30)
        .Future<Blog>();
    var countOfBlogs = s.CreateCriteria<Blog>()
        .SetProjection(Projections.Count(Projections.Id()))
        .FutureValue<int>();

    Console.WriteLine("Number of blogs: {0}", countOfBlogs.Value);
    foreach (var blog in blogs)
    {
        Console.WriteLine(blog.Title);
    }

    tx.Commit();
}

然后你可以这样称呼它:

private final int TIMEOUT_MILLISECONDS = 5000;
private final int SLEEP_MILLISECONDS = 100;
private int time = 0;
private boolean wasDisplayed = false;

public Boolean isVisible(ViewInteraction interaction) throws InterruptedException {
    interaction.withFailureHandler((error, viewMatcher) -> wasDisplayed = false);
    if (wasDisplayed) {
        return true;
    }
    if (time >= TIMEOUT_MILLISECONDS) {
        return false;
    }

    //set it to true if failing handle should set it to false again.
    wasDisplayed = true;
    Thread.sleep(SLEEP_MILLISECONDS);
    time += SLEEP_MILLISECONDS;

    interaction.check(matches(isDisplayed()));
    Log.i("ViewChecker","sleeping");
    return isVisible(interaction);
}