122

エスプレッソは必要ないと主張していThread.sleep()ますが、コードを含めないと機能しません。IP に接続していますが、接続中に進行状況ダイアログが表示されます。Thread.sleep()ダイアログが閉じるのを待つための呼び出しが必要です。これは私が使用するテストコードです:

    IP.enterIP(); // fills out an IP dialog (this is done with espresso)

    //progress dialog is now shown
    Thread.sleep(1500);

    onView(withId(R.id.button).perform(click());

Thread.sleep()呼び出しなしでこのコードを試しましたがR.id.Button、存在しないと表示されます。私がそれを機能させる唯一の方法は、Thread.sleep()呼び出しを使用することです。

Thread.sleep()また、のようなものに置き換えてみましたがgetInstrumentation().waitForIdleSync()、まだ運がありません。

これがこれを行う唯一の方法ですか?または、何か不足していますか?

前もって感謝します。

4

14 に答える 14

118

私の考えでは、正しいアプローチは次のようになります。

/** Perform action of waiting for a specific view id. */
public static ViewAction waitId(final int viewId, final long millis) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isRoot();
        }

        @Override
        public String getDescription() {
            return "wait for a specific view with id <" + viewId + "> during " + millis + " millis.";
        }

        @Override
        public void perform(final UiController uiController, final View view) {
            uiController.loopMainThreadUntilIdle();
            final long startTime = System.currentTimeMillis();
            final long endTime = startTime + millis;
            final Matcher<View> viewMatcher = withId(viewId);

            do {
                for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
                    // found view with required ID
                    if (viewMatcher.matches(child)) {
                        return;
                    }
                }

                uiController.loopMainThreadForAtLeast(50);
            }
            while (System.currentTimeMillis() < endTime);

            // timeout happens
            throw new PerformException.Builder()
                    .withActionDescription(this.getDescription())
                    .withViewDescription(HumanReadables.describe(view))
                    .withCause(new TimeoutException())
                    .build();
        }
    };
}

そして、使用パターンは次のようになります。

// wait during 15 seconds for a view
onView(isRoot()).perform(waitId(R.id.dialogEditor, TimeUnit.SECONDS.toMillis(15)));
于 2014-03-21T15:45:41.710 に答える
56

すばらしい回答をくれた AlexK に感謝します。コードを少し遅らせる必要がある場合があります。必ずしもサーバーの応答を待っているわけではありませんが、アニメーションが完了するのを待っている可能性があります。私は個人的にエスプレッソのidolingResourcesに問題があります(単純なことのために多くのコード行を書いていると思います)ので、AlexKが行っていた方法を次のコードに変更しました:

/**
 * Perform action of waiting for a specific time.
 */
public static ViewAction waitFor(final long millis) {
    return new ViewAction() {
        @Override
        public Matcher<View> getConstraints() {
            return isRoot();
        }

        @Override
        public String getDescription() {
            return "Wait for " + millis + " milliseconds.";
        }

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

したがって、Delay簡単にアクセスできるように、クラスを作成してこのメ​​ソッドをその中に入れることができます。同じ方法で Test クラスで使用できます。onView(isRoot()).perform(waitFor(5000));

于 2016-03-10T18:56:42.507 に答える
19

この行を追加する方が簡単だと思います:

SystemClock.sleep(1500);

指定されたミリ秒数 (uptimeMillis) 待機してから戻ります。sleep(long) に似ていますが、InterruptedException をスローしません。interrupt() イベントは、次の割り込み可能な操作まで延期されます。少なくとも指定されたミリ秒数が経過するまで戻りません。

于 2016-07-06T13:13:11.590 に答える
11

これはこの回答に似ていますが、試行の代わりにタイムアウトを使用し、他の ViewInteractions と連鎖させることができます:

/**
 * Wait for view to be visible
 */
fun ViewInteraction.waitUntilVisible(timeout: Long): ViewInteraction {
    val startTime = System.currentTimeMillis()
    val endTime = startTime + timeout

    do {
        try {
            check(matches(isDisplayed()))
            return this
        } catch (e: AssertionFailedError) {
            Thread.sleep(50)
        }
    } while (System.currentTimeMillis() < endTime)

    throw TimeoutException()
}

使用法:

onView(withId(R.id.whatever))
    .waitUntilVisible(5000)
    .perform(click())
于 2019-05-30T20:45:13.857 に答える
3

Espresso は、テストでの sleep() 呼び出しを回避するように構築されています。テストでは、IP を入力するためのダイアログを開くべきではありません。これは、テスト対象のアクティビティの責任である必要があります。

一方、UI テストは次のようにする必要があります。

  • IP ダイアログが表示されるまで待ちます
  • IPアドレスを入力してEnterをクリック
  • ボタンが表示されるのを待ってクリックします

テストは次のようになります。

// type the IP and press OK
onView (withId (R.id.dialog_ip_edit_text))
  .check (matches(isDisplayed()))
  .perform (typeText("IP-TO-BE-TYPED"));

onView (withText (R.string.dialog_ok_button_title))
  .check (matches(isDisplayed()))
  .perform (click());

// now, wait for the button and click it
onView (withId (R.id.button))
  .check (matches(isDisplayed()))
  .perform (click());

Espresso は、テストを実行する前に、UI スレッドと AsyncTask プールの両方で発生しているすべての処理が完了するのを待ちます。

テストでは、アプリケーションの責任となるようなことは何もすべきではないことに注意してください。「十分な情報を持ったユーザー」のように振る舞う必要があります。つまり、クリックして画面に何かが表示されていることを確認するユーザーですが、実際には、コンポーネントの ID を知っているユーザーです。

于 2014-01-29T10:55:58.580 に答える
2

このCodeLabで提案されている Espresso Idling Resource を使用する必要があります。

アイドリング リソースは、結果が UI テストの後続の操作に影響する非同期操作を表します。アイドリング リソースを Espresso に登録することで、アプリのテスト時にこれらの非同期操作をより確実に検証できます。

プレゼンターからの非同期呼び出しの例

@Override
public void loadNotes(boolean forceUpdate) {
   mNotesView.setProgressIndicator(true);
   if (forceUpdate) {
       mNotesRepository.refreshData();
   }

   // The network request might be handled in a different thread so make sure Espresso knows
   // that the app is busy until the response is handled.
   EspressoIdlingResource.increment(); // App is busy until further notice

   mNotesRepository.getNotes(new NotesRepository.LoadNotesCallback() {
       @Override
       public void onNotesLoaded(List<Note> notes) {
           EspressoIdlingResource.decrement(); // Set app as idle.
           mNotesView.setProgressIndicator(false);
           mNotesView.showNotes(notes);
       }
   });
}

依存関係

androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    implementation 'androidx.test.espresso:espresso-idling-resource:3.1.1'

androidxの場合

androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'com.android.support.test.espresso:espresso-idling-resource:3.0.2'

公式リポジトリ: https://github.com/googlecodelabs/android-testing

IdlingResource の例: https://github.com/googlesamples/android-testing/tree/master/ui/espresso/IdlingResourceSample

于 2019-02-26T14:15:16.507 に答える
-1

これを行う方法をミックスに追加します。

fun suspendUntilSuccess(actionToSucceed: () -> Unit, iteration : Int = 0) {
    try {
        actionToSucceed.invoke()
    } catch (e: Throwable) {
        Thread.sleep(200)
        val incrementedIteration : Int = iteration + 1
        if (incrementedIteration == 25) {
            fail("Failed after waiting for action to succeed for 5 seconds.")
        }
        suspendUntilSuccess(actionToSucceed, incrementedIteration)
    }
}

このように呼び出されます:

suspendUntilSuccess({
    checkThat.viewIsVisible(R.id.textView)
})

最大反復回数、反復長などのパラメーターを suspendUntilSuccess 関数に追加できます。

私はまだアイドリング リソースを使用することを好みますが、たとえば、デバイスのアニメーションが遅いためにテストがうまく機能しない場合は、この関数を使用するとうまく機能します。もちろん、失敗する前に最大 5 秒間ハングする可能性があるため、成功するためのアクションがまったく成功しない場合、テストの実行時間が長くなる可能性があります。

于 2019-08-31T00:38:03.393 に答える