1

I'm using Espresso to write some automated tests for an Android app that I've developed. All the tests are automated and are passing/failing according to what happens with the UI. I've ran the code through SonarQube to detect bad coding practices and it's informed me that Thread.Sleep() should not be used.

I'm mainly using Thread.sleep() in instances where I'm typing out a form and need to hide the keyboard to scroll down to tap the next form field etc. From my understanding, using something like awaitility is for big async functions like fetching data etc. but what should I use in my case where something is not being fetched but more so for just interacting with the UI?

Here is an example of a log in test that I have created that uses Thread.Sleep():

        onView(withId(R.id.fieldEmail)).perform(typeText("shelley@gmail.com"));
        Thread.sleep(SHORT_WAIT);
        onView(withId(R.id.fieldPassword)).perform(click());
        onView(withId(R.id.fieldPassword)).perform(typeText("password"));
        Thread.sleep(SHORT_WAIT);
        onView(isRoot()).perform(pressBack());
        Thread.sleep(SHORT_WAIT);
        onView(withId(R.id.signIn)).perform(click());
        Thread.sleep(LONG_WAIT);
rookiecookie
  • 129
  • 2
  • 11

1 Answers1

3

There are several options:

Repeated retries

You can use Awaitility to repeatedly retry an assertion/check, up to a specified time allowance:

app/build.gradle

dependencies {
    // Note: Awaitility version 4 has dependency conflicts with JUnit's
    // Hamcrest. See: https://github.com/awaitility/awaitility/issues/194
    androidTestImplementation 'org.awaitility:awaitility:3.1.6'
}
// Set the retry time to 0.5 seconds, instead of the default 0.1 seconds
Awaitility.setDefaultPollInterval(500, TimeUnit.MILLISECONDS);

Kotlin:
Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAsserted {
    onView(withId(R.id.fieldPassword)).perform(click())
}

Java 7:
Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAsserted(new ThrowingRunnable() {
    @Override
    public void run() throws Throwable {
        onView(withId(R.id.fieldPassword)).perform(click());
    }
}); 

This means that if the assertion fails the first time, it will retry for up to 2 seconds, until it's true or until a timeout happens (fail).

Repeated retries with an initial time delay

You can also set an initial time delay before making the first assertion:

Awaitility.await().pollDelay(1, TimeUnit.SECONDS).atMost(3, TimeUnit.SECONDS).untilAsserted {
    onView(withId(R.id.fieldPassword)).perform(click())
}

Simple time delay

Alternatively, you can add simple time delays in between statements, just like Thread.sleep(), but in a more verbose way:

Kotlin:
Awaitility.await().pollDelay(2, TimeUnit.SECONDS).until { true }

Java 7:
Awaitility.await().pollDelay(2, TimeUnit.SECONDS).until(new Callable<Boolean>() {
    @Override
    public Boolean call() throws Exception {
        return true;
    }
});

Use Espresso or Barista functions to create the timed waits:

Use Espresso Idling Resources


General note: Although using Thread.sleep() or some other time delay should normally be avoided in unit tests, there will be times where you need to use it, and there is no alternative. An example is using IntentsTestRule to click on a web link in your app to launch the external web browser. You don't know how long the browser will take to launch the web page, so you need to add a time delay.


More info about Awaitility:

Mr-IDE
  • 7,051
  • 1
  • 53
  • 59