Thread.sleep( ) mit Espresso

Lesezeit: 3 Minuten

Benutzeravatar von Chad Bingham
Chad Bingham

Espresso behauptet, dass es keine Notwendigkeit gibt Thread.sleep() aber mein Code funktioniert nicht, wenn ich ihn nicht einfüge. Ich verbinde mich mit einer IP und während der Verbindung wird ein Fortschrittsdialog angezeigt. ich brauche ein Thread.sleep() aufrufen, um zu warten, bis der Dialog geschlossen wird. Dies ist mein Testcode, wo ich ihn verwende:

    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());

Ich habe diesen Code ohne die versucht Thread.sleep() anrufen, aber es heißt R.id.Button existiert nicht. Die einzige Möglichkeit, wie ich es zum Laufen bekomme, ist mit der Thread.sleep() Anruf.

Außerdem habe ich versucht zu ersetzen Thread.sleep() mit Sachen wie getInstrumentation().waitForIdleSync() und immer noch kein Glück.

Ist dies der einzige Weg, dies zu tun? Oder übersehe ich etwas?

  • Ist es möglich, dass Sie eine unerwünschte While-Schleife setzen, ohne dass Sie den Anruf blockieren möchten.

    – kedark

    28. Januar 2014 um 22:45 Uhr

  • ok.. lass es mich erklären. 2 Vorschläge für Sie 1.) Implementieren Sie so etwas wie einen Rückrufmechanismus. on-connection-eater ruft eine Methode auf und zeigt die Ansicht . 2.) Sie möchten die Verzögerung zwischen IP.enterIP(); und onView(….), damit Sie die While-Schleife setzen können, die eine ähnliche Art von Verzögerung erzeugt, um onview(..) aufzurufen … aber ich denke, wenn möglich, bevorzugen Sie Option Nr. 1. (Erstellen des Rückrufs Mechanismus)…

    – kedark

    28. Januar 2014 um 23:09 Uhr

  • @kedark Ja, das ist eine Option, aber ist das die Lösung von Espresso?

    – Chad Bingham

    29. Januar 2014 um 0:59 Uhr

  • Ihre Frage enthält unbeantwortete Kommentare. Können Sie sie beantworten?

    – Bolhoso

    18. Februar 2014 um 12:10 Uhr

  • @Bolhoso, welche Frage?

    – Chad Bingham

    18. Februar 2014 um 16:28 Uhr

Benutzeravatar von Oleksandr Kucherenko
Oleksandr Kucherenko

Meiner Meinung nach wird der richtige Ansatz sein:

/** 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();
        }
    };
}

Und dann wird das Nutzungsmuster sein:

// wait during 15 seconds for a view
onView(isRoot()).perform(waitId(R.id.dialogEditor, TimeUnit.SECONDS.toMillis(15)));

  • Danke Alex, warum hast du diese Option gegenüber IdlingResource oder AsyncTasks gewählt?

    – Tim Boland

    2. Oktober 2014 um 22:06 Uhr

  • Dies ist ein Workaround-Ansatz, in den meisten Fällen erledigt Espresso die Arbeit ohne Probleme und einen speziellen ‘Wartecode’. Ich probiere tatsächlich verschiedene Möglichkeiten aus und denke, dass dies die am besten passende Espresso-Architektur / -Design ist.

    – Oleksandr Kucherenko

    6. Oktober 2014 um 8:40 Uhr


  • @AlexK das hat meinen Tagesgefährten gemacht!

    – Dawid Danski

    12. Februar 2016 um 16:02 Uhr

  • bei mir schlägt es für api <= 19 fehl, bei line throw new PerformException.Builder()

    – Prabin Timsina

    23. Juni 2016 um 20:45 Uhr

  • Ich hoffe, Sie verstehen, dass es sich um ein Beispiel handelt, das Sie kopieren/einfügen und für Ihre eigenen Bedürfnisse ändern können. Es liegt vollständig in Ihrer Verantwortung, es für Ihre eigenen geschäftlichen Anforderungen ordnungsgemäß zu verwenden, nicht in meiner.

    – Oleksandr Kucherenko

    5. Juli 2016 um 14:57 Uhr

Benutzeravatar von Hesam
Hesam

Danke an AlexK für seine tolle Antwort. Es gibt Fälle, in denen Sie den Code etwas verzögern müssen. Es wartet nicht unbedingt auf eine Serverantwort, sondern wartet möglicherweise darauf, dass die Animation abgeschlossen wird. Ich persönlich habe ein Problem mit Espresso idolingResources (ich denke, wir schreiben viele Codezeilen für eine einfache Sache), also habe ich die Vorgehensweise von AlexK in folgenden Code geändert:

/**
 * 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);
        }
    };
}

Sie können also eine erstellen Delay Klasse und fügen Sie diese Methode darin ein, um einfach darauf zugreifen zu können. Sie können es in Ihrer Testklasse auf die gleiche Weise verwenden: onView(isRoot()).perform(waitFor(5000));

  • Die Perform-Methode kann sogar mit einer Zeile wie dieser vereinfacht werden: uiController.loopMainThreadForAtLeast(millis);

    – Yair Kukielka

    18. Mai 2016 um 17:53 Uhr

  • Super, das wusste ich nicht :thumbs_up @YairKukielka

    – Hesam

    18. Mai 2016 um 18:13 Uhr


  • Huch für das geschäftige Warten.

    – TWiStErRob

    25. August 2016 um 11:35 Uhr

  • Großartig. Das habe ich ewig gesucht. +1 für eine einfache Lösung für Warteprobleme.

    – Tobias Reich

    16. November 2016 um 12:17 Uhr

  • Ich verstehe nicht, wie man das nennt ViewAction ist anders als zu telefonieren SystemClock.sleep(millis). Beide warten eine feste Anzahl von Millisekunden, bevor sie zurückkehren. Ich würde dringend empfehlen, Ihre zu definieren ViewAction Klassen, auf eine bestimmte Bedingung zu warten (wie hier und hier gezeigt), damit sie in den meisten Fällen schneller zurückkehren und im Fehlerfall nur bis zur maximalen Anzahl von Millisekunden warten.

    – Adil Hussain

    23. Oktober 2020 um 12:24 Uhr

MattMatts Benutzeravatar
MattMatt

Ich bin auf diesen Thread gestoßen, als ich nach einer Antwort auf ein ähnliches Problem gesucht habe, bei dem ich auf eine Serverantwort gewartet und die Sichtbarkeit von Elementen basierend auf der Antwort geändert habe.

Während die obige Lösung definitiv geholfen hat, fand ich sie schließlich dieses hervorragende beispiel von chiuki und verwende diesen Ansatz jetzt als meine Anlaufstelle, wenn ich darauf warte, dass während App-Leerlaufzeiten Aktionen ausgeführt werden.

Ich habe hinzugefügt ElapsedTimeIdlingResource() zu meiner eigenen Utilities-Klasse, kann das jetzt effektiv als Espresso-geeignete Alternative verwenden, und jetzt ist die Verwendung schön und sauber:

// Make sure Espresso does not time out
IdlingPolicies.setMasterPolicyTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
IdlingPolicies.setIdlingResourceTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);

// Now we wait
IdlingResource idlingResource = new ElapsedTimeIdlingResource(waitingTime);
Espresso.registerIdlingResources(idlingResource);

// Stop and verify
onView(withId(R.id.toggle_button))
    .check(matches(withText(R.string.stop)))
    .perform(click());
onView(withId(R.id.result))
    .check(matches(withText(success ? R.string.success: R.string.failure)));

// Clean up
Espresso.unregisterIdlingResources(idlingResource);

  • Ich bekomme ein I/TestRunner: java.lang.NoClassDefFoundError: fr.x.app.y.testtools.ElapsedTimeIdlingResourceError. Irgendeine Idee. Ich benutze Proguard, aber mit deaktivierter Verschleierung.

    – Antonius

    25. April 2016 um 18:20 Uhr

  • Versuchen Sie, a hinzuzufügen -keep Anweisung für Klassen, die nicht gefunden werden, um sicherzustellen, dass ProGuard sie nicht als unnötig entfernt. Mehr Infos hier: developer.android.com/tools/help/proguard.html#keep-code

    – MattMatt

    26. April 2016 um 9:24 Uhr


  • Ich poste eine Frage stackoverflow.com/questions/36859528/…. Die Klasse befindet sich in der seed.txt und der mapping.txt

    – Antonius

    26. April 2016 um 10:21 Uhr

  • Wenn Sie die Leerlaufrichtlinien ändern müssen, implementieren Sie die Leerlaufressourcen wahrscheinlich nicht richtig. Langfristig ist es viel besser, Zeit zu investieren, um das zu beheben. Diese Methode führt schließlich zu langsamen und unstabilen Tests. Kasse google.github.io/android-testing-support-library/docs/espresso/…

    – José Alcérreca

    28. September 2016 um 9:27 Uhr

  • Du liegst richtig. Diese Antwort ist über ein Jahr alt, und seitdem hat sich das Verhalten von Ressourcen im Leerlauf so verbessert, dass derselbe Anwendungsfall, für den ich den obigen Code verwendet habe, jetzt sofort funktioniert und den verspotteten API-Client ordnungsgemäß erkennt – wir verwenden den oben genannten nicht mehr ElapsedTimeIdlingResource in unseren instrumentierten Tests aus diesem Grund. (Sie könnten natürlich auch alle Dinge Rxen, was die Notwendigkeit, in einer Wartezeit zu hacken, zunichte macht). Allerdings ist die Vorgehensweise von Google nicht immer die beste: philosophicalhacker.com/post/….

    – MattMatt

    28. September 2016 um 18:55 Uhr

Benutzeravatar von Cabezas
Cabezas

Ich denke, es ist einfacher, diese Zeile hinzuzufügen:

SystemClock.sleep(1500);

Wartet vor der Rückkehr eine bestimmte Anzahl von Millisekunden (von uptimeMillis). Ähnlich wie sleep(long), löst aber keine InterruptedException aus; interrupt()-Ereignisse werden bis zur nächsten unterbrechbaren Operation verschoben. Kehrt erst zurück, wenn mindestens die angegebene Anzahl von Millisekunden verstrichen ist.

Benutzeravatar von Big McLargeHuge
Big McLargeHuge

Dies ähnelt dieser Antwort, verwendet jedoch ein Timeout anstelle von Versuchen und kann mit anderen ViewInteractions verkettet werden:

/**
 * 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()
}

Verwendung:

onView(withId(R.id.whatever))
    .waitUntilVisible(5000)
    .perform(click())

  • Ich habe diesen Ansatz verwendet, aber es hat nicht genau für mich funktioniert. Ich musste AssertionFailedError anstelle von NoMatchingViewException abfangen. Mit dieser Änderung funktioniert es einwandfrei

    – moyo

    14. September 2021 um 13:41 Uhr

Benutzeravatar von Roc Boronat
Roc Boronat

Sie können einfach Barista-Methoden verwenden:

BaristaSleepActions.sleep(2000);

BaristaSleepActions.sleep(2, SECONDS);

Barista ist eine Bibliothek, die Espresso umschließt, um zu vermeiden, dass der gesamte Code hinzugefügt wird, der für die akzeptierte Antwort erforderlich ist. Und hier ist ein Link! https://github.com/SchibstedSpain/Barista

  • Ich habe diesen Ansatz verwendet, aber es hat nicht genau für mich funktioniert. Ich musste AssertionFailedError anstelle von NoMatchingViewException abfangen. Mit dieser Änderung funktioniert es perfekt

    – moyo

    14. September 2021 um 13:41 Uhr

Benutzeravatar von anna3101
Anna3101

Ich bin neu in Codierung und Espresso, also weiß ich, dass die Verwendung des Leerlaufs eine gute und vernünftige Lösung ist, aber ich bin noch nicht intelligent genug, um das zu tun.

Bis ich sachkundiger werde, brauche ich immer noch meine Tests, um irgendwie zu laufen, also benutze ich jetzt diese schmutzige Lösung, die eine Reihe von Versuchen unternimmt, ein Element zu finden, stoppt, wenn es es findet, und wenn nicht, schläft es kurz und startet erneut, bis die maximale Anzahl von Versuchen erreicht ist (die höchste Anzahl von Versuchen lag bisher bei etwa 150).

private static boolean waitForElementUntilDisplayed(ViewInteraction element) {
    int i = 0;
    while (i++ < ATTEMPTS) {
        try {
            element.check(matches(isDisplayed()));
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            try {
                Thread.sleep(WAITING_TIME);
            } catch (Exception e1) {
                e.printStackTrace();
            }
        }
    }
    return false;
}

Ich verwende dies in allen Methoden, die Elemente nach ID, Text, Eltern usw. finden:

static ViewInteraction findById(int itemId) {
    ViewInteraction element = onView(withId(itemId));
    waitForElementUntilDisplayed(element);
    return element;
}

  • in deinem Beispiel die findById(int itemId) -Methode gibt ein Element zurück (das NULL sein könnte), ob die waitForElementUntilDisplayed(element); gibt wahr oder falsch zurück …. also, das ist nicht in Ordnung

    – mbob

    10. November 2017 um 15:23 Uhr


  • Ich wollte mich nur einmischen und sagen, dass dies meiner Meinung nach die beste Lösung ist. IdlingResources reichen mir aufgrund der Granularität der Abfragerate von 5 Sekunden nicht aus (viel zu groß für meinen Anwendungsfall). Die akzeptierte Antwort funktioniert auch bei mir nicht (eine Erklärung dafür ist bereits im langen Kommentar-Feed dieser Antwort enthalten). Danke dafür! Ich nahm Ihre Idee und machte meine eigene Lösung und es funktioniert wie ein Zauber.

    – oaskamay

    13. Dezember 2017 um 21:16 Uhr

  • Ja, dies ist die einzige Lösung, die auch bei mir funktioniert hat, wenn ich auf Elemente warten möchte, die nicht in der aktuellen Aktivität enthalten sind.

    – guilhermekrz

    19. Dezember 2017 um 15:04 Uhr

  • Bei mir funktioniert das nicht, auch mit Try-Catch-Block zeigte der Test einen Fehler (weil jede Ausnahme verhindert, dass die Testergebnisse in Ordnung sind). Für mich kombiniere ich den rekursiven Ansatz mit Thread Sleep und mit FailureHandler, was gut funktioniert.

    – Thiago Neves

    20. August 2021 um 13:07 Uhr

1439140cookie-checkThread.sleep( ) mit Espresso

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy