Espresso: Thread.sleep ();

Espresso claims that there is no need for Thread.sleep(); but my code does not work unless I enable it. I am connecting to IP. When connected, a progress dialog is displayed. I need sleep to wait for the dialog to decline. This is my test snippet where I use it:

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

I tried this code with Thread.sleep(); and without him, but he says that R.id.Button does not exist. The only way to make it work is to sleep.

In addition, I tried replacing Thread.sleep(); on things like getInstrumentation().waitForIdleSync(); and still no luck.

Is this the only way to do this? Or am I missing something?

Thanks in advance.

+89
android testing android-espresso
Jan 28 '14 at 22:06
source share
13 answers

In my opinion, the correct approach would be:

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

And then the usage pattern will be:

 // wait during 15 seconds for a view onView(isRoot()).perform(waitId(R.id.dialogEditor, TimeUnit.SECONDS.toMillis(15))); 
+102
Mar 21 '14 at 15:45
source share

Thanks to AlexK for his wonderful answer. There are times when you need to delay the code a bit. This does not necessarily wait for the server to respond, but it can wait for the animation. I personally have a problem with Espresso idolingResources (I think we are writing a lot of lines of code for the simple thing), so I changed the way AlexK handled the following code:

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

So, you can create a Delay class and put this method in it to easily get it. You can use it in your test class in the same way: onView(isRoot()).perform(waitFor(5000));

+42
Mar 10 '16 at 18:56
source share

I came across this thread when I was looking for an answer to a similar problem, where I was waiting for a server response and changing the visibility of elements based on the response.

Despite the fact that the solution certainly helped, I eventually found this great example from chiuki and now use this approach as my option, m, waiting for actions to be taken during periods of application downtime.

I added ElapsedTimeIdlingResource () to my own utility class, now it can be effectively used as a suitable alternative to espresso, and now it’s nice and clean to use:

 // 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); 
+20
Jul 20 '15 at 16:08
source share

I think adding this line is easier:

 SystemClock.sleep(1500); 

Waits for the specified number of milliseconds (from uptimeMillis) before returning. Like sleep (long), but does not throw an InterruptedException; interrupt () is delayed until the next intermittent operation. It does not return until the specified number of milliseconds has elapsed.

+14
Jul 06 '16 at 13:13
source share

You can simply use the Barista methods:

BaristaSleepActions.sleep(2000);

BaristaSleepActions.sleep(2, SECONDS);

Barista is the library that packs Espresso to avoid adding all the code needed for the accepted answer. And here is the link! https://github.com/SchibstedSpain/Barista

+7
Mar 30 '17 at 15:54
source share

I am new to coding and espresso, so although I know that a good and reasonable solution is to use idle speed, I'm not smart enough to do it yet.

Until I become more knowledgeable, I still need my tests to somehow run, so while I use this dirty solution, which makes a number of attempts to find an element, it stops if it finds it, and if not, it sleeps briefly and starts again until it reaches the maximum number of attempts (the largest number of attempts so far has been about 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; } 

I use this in all methods of searching for elements by identifier, text, parent element, etc.:

 static ViewInteraction findById(int itemId) { ViewInteraction element = onView(withId(itemId)); waitForElementUntilDisplayed(element); return element; } 
+4
Jun 02 '17 at 10:55 on
source share

Espresso is built to avoid sleep () calls in tests. Your test should not open a dialog box for entering an IP address, which should be a check of activity.

On the other hand, your user interface test should:

  • Wait for the IP dialog to appear.
  • Fill in the IP address and click
  • Wait until your button appears and click on it.

The test should look something like this:

 // 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 waits for everything that happens both in the user interface thread and in the AsyncTask pool to complete the job before running your tests.

Remember that your tests should not do anything that suits your applications. It should behave like a "well-informed user": the user who clicks checks that something is displayed on the screen, but, in fact, knows the identifiers of the components

+3
Jan 29 '14 at 10:55
source share

You must use Espresso Idling Resource, this is suggested in CodeLab

The idle resource represents an asynchronous operation, the results of which affect subsequent operations in a user interface test. When registering idle resources with Espresso, you can test these asynchronous more reliable operations when testing the application.

Presenter Asynchronous Call Example

 @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); } }); } 

addictions

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

for androidx

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

Official repo: https://github.com/googlecodelabs/android-testing

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

+1
Feb 26 '19 at 14:15
source share

This is similar to this answer , but uses a timeout instead of attempts and may be related to other 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: NoMatchingViewException) { Thread.sleep(50) } } while (System.currentTimeMillis() < endTime) throw TimeoutException() } 

Using:

 onView(withId(R.id.whatever)) .waitUntilVisible(5000) .perform(click()) 
+1
May 30 '19 at 20:45
source share

While I find it better to use resources for idle ( https://google.imtqy.com/android-testing-support-library/docs/espresso/idling-resource/ ), you can probably use this as a reserve:

 /** * Contains view interactions, view actions and view assertions which allow to set a timeout * for finding a view and performing an action/view assertion on it. * To be used instead of {@link Espresso} methods. * * @author Piotr Zawadzki */ public class TimeoutEspresso { private static final int SLEEP_IN_A_LOOP_TIME = 50; private static final long DEFAULT_TIMEOUT_IN_MILLIS = 10 * 1000L; /** * Use instead of {@link Espresso#onView(Matcher)} * @param timeoutInMillis timeout after which an error is thrown * @param viewMatcher view matcher to check for view * @return view interaction */ public static TimedViewInteraction onViewWithTimeout(long timeoutInMillis, @NonNull final Matcher<View> viewMatcher) { final long startTime = System.currentTimeMillis(); final long endTime = startTime + timeoutInMillis; do { try { return new TimedViewInteraction(Espresso.onView(viewMatcher)); } catch (NoMatchingViewException ex) { //ignore } SystemClock.sleep(SLEEP_IN_A_LOOP_TIME); } while (System.currentTimeMillis() < endTime); // timeout happens throw new PerformException.Builder() .withCause(new TimeoutException("Timeout occurred when trying to find: " + viewMatcher.toString())) .build(); } /** * Use instead of {@link Espresso#onView(Matcher)}. * Same as {@link #onViewWithTimeout(long, Matcher)} but with the default timeout {@link #DEFAULT_TIMEOUT_IN_MILLIS}. * @param viewMatcher view matcher to check for view * @return view interaction */ public static TimedViewInteraction onViewWithTimeout(@NonNull final Matcher<View> viewMatcher) { return onViewWithTimeout(DEFAULT_TIMEOUT_IN_MILLIS, viewMatcher); } /** * A wrapper around {@link ViewInteraction} which allows to set timeouts for view actions and assertions. */ public static class TimedViewInteraction { private ViewInteraction wrappedViewInteraction; public TimedViewInteraction(ViewInteraction wrappedViewInteraction) { this.wrappedViewInteraction = wrappedViewInteraction; } /** * @see ViewInteraction#perform(ViewAction...) */ public TimedViewInteraction perform(final ViewAction... viewActions) { wrappedViewInteraction.perform(viewActions); return this; } /** * {@link ViewInteraction#perform(ViewAction...)} with a timeout of {@link #DEFAULT_TIMEOUT_IN_MILLIS}. * @see ViewInteraction#perform(ViewAction...) */ public TimedViewInteraction performWithTimeout(final ViewAction... viewActions) { return performWithTimeout(DEFAULT_TIMEOUT_IN_MILLIS, viewActions); } /** * {@link ViewInteraction#perform(ViewAction...)} with a timeout. * @see ViewInteraction#perform(ViewAction...) */ public TimedViewInteraction performWithTimeout(long timeoutInMillis, final ViewAction... viewActions) { final long startTime = System.currentTimeMillis(); final long endTime = startTime + timeoutInMillis; do { try { return perform(viewActions); } catch (RuntimeException ex) { //ignore } SystemClock.sleep(SLEEP_IN_A_LOOP_TIME); } while (System.currentTimeMillis() < endTime); // timeout happens throw new PerformException.Builder() .withCause(new TimeoutException("Timeout occurred when trying to perform view actions: " + viewActions)) .build(); } /** * @see ViewInteraction#withFailureHandler(FailureHandler) */ public TimedViewInteraction withFailureHandler(FailureHandler failureHandler) { wrappedViewInteraction.withFailureHandler(failureHandler); return this; } /** * @see ViewInteraction#inRoot(Matcher) */ public TimedViewInteraction inRoot(Matcher<Root> rootMatcher) { wrappedViewInteraction.inRoot(rootMatcher); return this; } /** * @see ViewInteraction#check(ViewAssertion) */ public TimedViewInteraction check(final ViewAssertion viewAssert) { wrappedViewInteraction.check(viewAssert); return this; } /** * {@link ViewInteraction#check(ViewAssertion)} with a timeout of {@link #DEFAULT_TIMEOUT_IN_MILLIS}. * @see ViewInteraction#check(ViewAssertion) */ public TimedViewInteraction checkWithTimeout(final ViewAssertion viewAssert) { return checkWithTimeout(DEFAULT_TIMEOUT_IN_MILLIS, viewAssert); } /** * {@link ViewInteraction#check(ViewAssertion)} with a timeout. * @see ViewInteraction#check(ViewAssertion) */ public TimedViewInteraction checkWithTimeout(long timeoutInMillis, final ViewAssertion viewAssert) { final long startTime = System.currentTimeMillis(); final long endTime = startTime + timeoutInMillis; do { try { return check(viewAssert); } catch (RuntimeException ex) { //ignore } SystemClock.sleep(SLEEP_IN_A_LOOP_TIME); } while (System.currentTimeMillis() < endTime); // timeout happens throw new PerformException.Builder() .withCause(new TimeoutException("Timeout occurred when trying to check: " + viewAssert.toString())) .build(); } } } 

and then call it in your code, for example:

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

instead

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

It also allows you to add timeouts to view actions and view claims.

0
Mar 17 '17 at 14:18
source share

My utility repeats the functions that are executed or called until it passes without errors or generates a throwable after a timeout. It works great for espresso tests!

Suppose that the last type of interaction (pressing a button) activates some background threads (network, database, etc.). As a result, a new screen should appear, and we want to check it in the next step, but we do not know when the new screen will be ready for testing.

The recommended approach is to get your application to send thread status messages to your test. Sometimes we can use built-in mechanisms such as OkHttp3IdlingResource. In other cases, you should embed code fragments in different places of the source code of your application (you should know the application logic!) Only to support testing. Moreover, we must disable all your animations (although this is part of the user interface).

Another approach awaits, for example, SystemClock.sleep (10000). But we do not know how long to wait, and even long delays cannot guarantee success. On the other hand, your test will last a long time.

My approach is to add a time condition to view the interaction. For example. we check that a new screen should appear within 10,000 ms (timeout). But we do not wait and check it as fast as we want (for example, every 100 ms) Of course, we block the test stream in this way, but usually this is exactly what we need in such cases.

 Usage: long timeout=10000; long matchDelay=100; //(check every 100 ms) EspressoExecutor myExecutor = new EspressoExecutor<ViewInteraction>(timeout, matchDelay); ViewInteraction loginButton = onView(withId(R.id.login_btn)); loginButton.perform(click()); myExecutor.callForResult(()->onView(allOf(withId(R.id.title),isDisplayed()))); 

This is my source in the class:

 /** * Created by alexshr on 02.05.2017. */ package com.skb.goodsapp; import android.os.SystemClock; import android.util.Log; import java.util.Date; import java.util.concurrent.Callable; /** * The utility repeats runnable or callable executing until it pass without errors or throws throwable after timeout. * It works perfectly for Espresso tests. * <p> * Suppose the last view interaction (button click) activates some background threads (network, database etc.). * As the result new screen should appear and we want to check it in our next step, * but we don't know when new screen will be ready to be tested. * <p> * Recommended approach is to force your app to send messages about threads states to your test. * Sometimes we can use built-in mechanisms like OkHttp3IdlingResource. * In other cases you should insert code pieces in different places of your app sources (you should known app logic!) for testing support only. * Moreover, we should turn off all your animations (although it the part on ui). * <p> * The other approach is waiting, eg SystemClock.sleep(10000). But we don't known how long to wait and even long delays can't guarantee success. * On the other hand your test will last long. * <p> * My approach is to add time condition to view interaction. Eg we test that new screen should appear during 10000 mc (timeout). * But we don't wait and check new screen as quickly as it appears. * Of course, we block test thread such way, but usually it just what we need in such cases. * <p> * Usage: * <p> * long timeout=10000; * long matchDelay=100; //(check every 100 ms) * EspressoExecutor myExecutor = new EspressoExecutor<ViewInteraction>(timeout, matchDelay); * <p> * ViewInteraction loginButton = onView(withId(R.id.login_btn)); * loginButton.perform(click()); * <p> * myExecutor.callForResult(()->onView(allOf(withId(R.id.title),isDisplayed()))); */ public class EspressoExecutor<T> { private static String LOG = EspressoExecutor.class.getSimpleName(); public static long REPEAT_DELAY_DEFAULT = 100; public static long BEFORE_DELAY_DEFAULT = 0; private long mRepeatDelay;//delay between attempts private long mBeforeDelay;//to start attempts after this initial delay only private long mTimeout;//timeout for view interaction private T mResult; /** * @param timeout timeout for view interaction * @param repeatDelay - delay between executing attempts * @param beforeDelay - to start executing attempts after this delay only */ public EspressoExecutor(long timeout, long repeatDelay, long beforeDelay) { mRepeatDelay = repeatDelay; mBeforeDelay = beforeDelay; mTimeout = timeout; Log.d(LOG, "created timeout=" + timeout + " repeatDelay=" + repeatDelay + " beforeDelay=" + beforeDelay); } public EspressoExecutor(long timeout, long repeatDelay) { this(timeout, repeatDelay, BEFORE_DELAY_DEFAULT); } public EspressoExecutor(long timeout) { this(timeout, REPEAT_DELAY_DEFAULT); } /** * call with result * * @param callable * @return callable result * or throws RuntimeException (test failure) */ public T call(Callable<T> callable) { call(callable, null); return mResult; } /** * call without result * * @param runnable * @return void * or throws RuntimeException (test failure) */ public void call(Runnable runnable) { call(runnable, null); } private void call(Object obj, Long initialTime) { try { if (initialTime == null) { initialTime = new Date().getTime(); Log.d(LOG, "sleep delay= " + mBeforeDelay); SystemClock.sleep(mBeforeDelay); } if (obj instanceof Callable) { Log.d(LOG, "call callable"); mResult = ((Callable<T>) obj).call(); } else { Log.d(LOG, "call runnable"); ((Runnable) obj).run(); } } catch (Throwable e) { long remain = new Date().getTime() - initialTime; Log.d(LOG, "remain time= " + remain); if (remain > mTimeout) { throw new RuntimeException(e); } else { Log.d(LOG, "sleep delay= " + mRepeatDelay); SystemClock.sleep(mRepeatDelay); call(obj, initialTime); } } } } 

https://gist.github.com/alexshr/ca90212e49e74eb201fbc976255b47e0

0
May 2 '17 at 5:36
source share

This is the helper that I use in Kotlin for Android tests. In my case, I use longOperation to simulate a server response, but you can configure it for your purpose.

 @Test fun ensureItemDetailIsCalledForRowClicked() { onView(withId(R.id.input_text)) .perform(ViewActions.typeText(""), ViewActions.closeSoftKeyboard()) onView(withId(R.id.search_icon)).perform(ViewActions.click()) longOperation( longOperation = { Thread.sleep(1000) }, callback = {onView(withId(R.id.result_list)).check(isVisible())}) } private fun longOperation( longOperation: ()-> Unit, callback: ()-> Unit ){ Thread{ longOperation() callback() }.start() } 
0
Feb 26 '19 at 14:01
source share

I will add my way to do this in the mix:

 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) } } 

It is called like this:

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

You can add parameters such as the maximum number of iterations, duration, etc., to the suspendUntilSuccess function.

I still prefer to use resources at idle, but when the tests work, for example, due to the slow animation on the device, I use this function and it works well. Of course, it can freeze for up to 5 seconds, just like before a crash, so it can increase the execution time of your tests if a successful action is never successful.

0
Aug 31 '19 at 0:38
source share



All Articles