Thread.sleep( ) con Espresso

3 minutos de lectura

Avatar de usuario de Chad Bingham
Chad Bingham

Espresso afirma que no hay necesidad de Thread.sleep() pero mi código no funciona a menos que lo incluya. Me estoy conectando a una IP y, mientras me conecto, se muestra un diálogo de progreso. necesito un Thread.sleep() llamar para esperar a que se cierre el cuadro de diálogo. Este es mi código de prueba donde lo uso:

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

He probado este código sin el Thread.sleep() llama pero dice R.id.Button no existe La única forma en que puedo hacer que funcione es con el Thread.sleep() llamada.

Además, he intentado reemplazar Thread.sleep() con cosas como getInstrumentation().waitForIdleSync() y todavía sin suerte.

¿Es esta la única manera de hacer esto? ¿O me estoy perdiendo algo?

  • ¿Es posible que coloque un ciclo while no deseado de todos modos? ¿Desea bloquear la llamada?

    – kedark

    28/01/2014 a las 22:45

  • ok.. déjame explicarte. 2 sugerencias para usted 1st) Implemente algo como un tipo de mecanismo de devolución de llamada. on-connection-establecer llame a un método y muestre la vista. 2º) desea crear el retraso entre IP.enterIP(); y onView(….) para que pueda poner el ciclo while que creará el tipo de retraso similar para llamar a onview(…) … pero creo que, si es posible, prefiera la opción No 1. (creando la devolución de llamada mecanismo)…

    – kedark

    28 de enero de 2014 a las 23:09

  • @kedark Sí, esa es una opción, pero ¿es esa la solución de Espresso?

    – Chad Bingham

    29 de enero de 2014 a las 0:59

  • Hay comentarios sin respuesta en su pregunta, ¿podría responderlos?

    – Bolhoso

    18 de febrero de 2014 a las 12:10

  • @Bolhoso, ¿qué pregunta?

    – Chad Bingham

    18 de febrero de 2014 a las 16:28

Avatar de usuario de Oleksandr Kucherenko
Oleksandr Kucherenko

En mi mente, el enfoque correcto será:

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

Y luego el patrón de uso será:

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

  • Gracias Alex, ¿por qué elegiste esta opción en lugar de IdlingResource o AsyncTasks?

    – Tim Boland

    2 oct 2014 a las 22:06

  • Este es un enfoque de solución alternativa, en la mayoría de los casos, Espresso hace el trabajo sin ningún problema y tiene un “código de espera” especial. De hecho, pruebo varias formas diferentes, y creo que esta es la arquitectura/diseño de Espresso que más combina.

    –Oleksandr Kucherenko

    6 oct 2014 a las 8:40


  • @AlexK ¡esto me alegró el día, amigo!

    – dawid gdanski

    12 de febrero de 2016 a las 16:02

  • para mí, falla para api

    – Prabin Timsina

    23/06/2016 a las 20:45

  • Espero que entiendas que es una muestra, puedes copiar/pegar y modificar según tus propias necesidades. Es completamente su responsabilidad usarlo correctamente en las necesidades de su propio negocio, no en las mías.

    –Oleksandr Kucherenko

    5 de julio de 2016 a las 14:57

Avatar de usuario de Hesam
Hesam

Gracias a AlexK por su increíble respuesta. Hay casos en los que necesita hacer algún retraso en el código. No necesariamente está esperando la respuesta del servidor, pero podría estar esperando que se realice la animación. Personalmente, tengo un problema con Espresso idolingResources (creo que estamos escribiendo muchas líneas de código para algo simple), así que cambié la forma en que AlexK estaba haciendo el siguiente código:

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

Así que puedes crear un Delay class y coloque este método en él para acceder a él fácilmente. Puede usarlo en su clase de prueba de la misma manera: onView(isRoot()).perform(waitFor(5000));

  • el método perform puede incluso simplificarse con una línea como esta: uiController.loopMainThreadForAtLeast(millis);

    – Yair Kukielka

    18 mayo 2016 a las 17:53

  • Impresionante, no sabía eso :thumbs_up @YairKukielka

    – Hesam

    18 mayo 2016 a las 18:13


  • Uy por la espera ocupada.

    – TWiStErRob

    25 de agosto de 2016 a las 11:35

  • Impresionante. Estuve buscando eso durante años. +1 para una solución simple para problemas de espera.

    – Tobías Reich

    16 de noviembre de 2016 a las 12:17

  • no veo como llamar a esto ViewAction es diferente a llamar SystemClock.sleep(millis). Ambos esperan un número fijo de milisegundos antes de regresar. Sugeriría enfáticamente que defina su ViewAction clases para esperar en una condición particular (como se demuestra aquí y aquí) por lo que regresan más rápido en la mayoría de los casos y solo esperan hasta el número máximo de milisegundos en caso de error.

    –Adil Hussein

    23 de octubre de 2020 a las 12:24

Avatar de usuario de MattMatt
MattMatt

Me topé con este hilo cuando buscaba una respuesta a un problema similar en el que estaba esperando una respuesta del servidor y cambiando la visibilidad de los elementos según la respuesta.

Si bien la solución anterior definitivamente ayudó, finalmente encontré este excelente ejemplo de chiuki y ahora uso ese enfoque como mi recurso cuando estoy esperando que ocurran acciones durante los períodos de inactividad de la aplicación.

he añadido ElapsedTimeIdlingResource() a mi propia clase de utilidades, ahora puedo usarlo de manera efectiva como una alternativa adecuada para Espresso, y ahora el uso es agradable y limpio:

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

  • Yo tengo un I/TestRunner: java.lang.NoClassDefFoundError: fr.x.app.y.testtools.ElapsedTimeIdlingResourceerror. Alguna idea. Uso Proguard pero con ofuscación deshabilitada.

    – Antonio

    25/04/2016 a las 18:20

  • Intenta agregar un -keep declaración para las clases que no se encuentran para asegurarse de que ProGuard no las elimine como innecesarias. Más información aquí: developer.android.com/tools/help/proguard.html#keep-code

    – MattMatt

    26 de abril de 2016 a las 9:24


  • Publico una pregunta stackoverflow.com/questions/36859528/…. La clase está en seed.txt y mapping.txt

    – Antonio

    26 de abril de 2016 a las 10:21

  • Si necesita cambiar las políticas de inactividad, probablemente no esté implementando correctamente los recursos de inactividad. A la larga es mucho mejor invertir tiempo en arreglar eso. Este método eventualmente conducirá a pruebas lentas y escamosas. Verificar google.github.io/android-testing-support-library/docs/espresso/…

    – José Alcérreca

    28 de septiembre de 2016 a las 9:27

  • Tienes toda la razón. Esta respuesta tiene más de un año y, desde entonces, el comportamiento de los recursos inactivos ha mejorado de tal manera que el mismo caso de uso que usé en el código anterior ahora funciona de manera inmediata, detectando correctamente el cliente API simulado: ya no usamos el anterior ElapsedTimeIdlingResource en nuestras pruebas instrumentadas por ese motivo. (Por supuesto, también podría Rx todas las cosas, lo que niega la necesidad de piratear en un período de espera). Dicho esto, la forma en que Google hace las cosas no siempre es la mejor: filosoficohacker.com/post/….

    – MattMatt

    28/09/2016 a las 18:55

Avatar de usuario de Cabezas
Cabezas

Creo que es más fácil agregar esta línea:

SystemClock.sleep(1500);

Espera un número dado de milisegundos (de uptimeMillis) antes de regresar. Similar a sleep(largo), pero no arroja InterruptedException; Los eventos de interrupción () se difieren hasta la siguiente operación interrumpible. No regresa hasta que haya transcurrido al menos el número especificado de milisegundos.

Avatar de usuario de Big McLargeHuge
Gran McLargeEnorme

Esto es similar a esta respuesta, pero usa un tiempo de espera en lugar de intentos y se puede encadenar con otras 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()
}

Uso:

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

  • Utilicé este enfoque, pero no funcionó exactamente para mí. Tuve que detectar AssertionFailedError en lugar de NoMatchingViewException. Con ese cambio funciona perfecto

    – moyo

    14/09/2021 a las 13:41

Avatar de usuario de Roc Boronat
Roc Boronat

Puedes usar los métodos de Barista:

BaristaSleepActions.sleep(2000);

BaristaSleepActions.sleep(2, SECONDS);

Barista es una biblioteca que envuelve Espresso para evitar agregar todo el código que necesita la respuesta aceptada. ¡Y aquí hay un enlace! https://github.com/SchibstedSpain/Barista

  • Utilicé este enfoque, pero no funcionó exactamente para mí. Tuve que detectar AssertionFailedError en lugar de NoMatchingViewException. Con ese cambio funciona perfecto

    – moyo

    14/09/2021 a las 13:41

avatar de usuario de anna3101
ana3101

Soy nuevo en la codificación y Espresso, así que aunque sé que la solución buena y razonable es usar el ralentí, todavía no soy lo suficientemente inteligente como para hacerlo.

Sin embargo, hasta que tenga más conocimientos, todavía necesito que mis pruebas se ejecuten de alguna manera, así que por ahora estoy usando esta solución sucia que hace varios intentos para encontrar un elemento, se detiene si lo encuentra y, si no lo encuentra, duerme brevemente y comienza nuevamente hasta alcanzar el número máximo de intentos (el mayor número de intentos hasta ahora ha sido alrededor de 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;
}

Estoy usando esto en todos los métodos que buscan elementos por ID, texto, padre, etc.:

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

  • en tu ejemplo, el findById(int itemId) método devolverá un elemento (que podría ser NULL) si el waitForElementUntilDisplayed(element); devuelve verdadero o falso… entonces, eso no está bien

    – mbob

    10 de noviembre de 2017 a las 15:23


  • Solo quería intervenir y decir que, en mi opinión, esta es la mejor solución. IdlingResources no son suficientes para mí debido a la granularidad de la tasa de sondeo de 5 segundos (demasiado grande para mi caso de uso). La respuesta aceptada tampoco funciona para mí (la explicación de por qué ya está incluida en el feed de comentarios largos de esa respuesta). ¡Gracias por esto! Tomé tu idea e hice mi propia solución y funciona de maravilla.

    – oaskamay

    13 de diciembre de 2017 a las 21:16

  • Sí, esta es la única solución que funcionó para mí también, al querer esperar elementos que no están en la actividad actual.

    – guilhermekrz

    19 de diciembre de 2017 a las 15:04

  • Para mí no funciona, incluso con el bloque try-catch, la prueba mostró falla (porque cualquier excepción, evita que los resultados de la prueba sean correctos). Para mí, combino el enfoque recursivo con Thread Sleep y con FailureHandler, que funciona bien.

    – Thiago Neves

    20 de agosto de 2021 a las 13:07

¿Ha sido útil esta solución?