Broad variety of waiting in Espresso
What is «Waiting»?
Lifeguard from flaky tests.
What variety do we have?
Hmm, let me think… 🤔
We knew about Idling Resources
Pros:
- It’s the most honest, fastest, and technically perfect way to wait;
- It doesn’t eat up the memory.
Cons:
- Sometimes it’s not a bit easier than writing production code;
- Sometimes we’ll have to touch production code;
- Most cases will probably require the own Idling Resources.
We heard about sleeps
Pros:
- Kill me then.
Cons:
- We’ll probably wait longer than we need;
- We’ll perhaps wait less than we need.
We were guessing about the hacky way of injecting UiAutomator
Pros:
- That’s something close to us since the time of Selenium.
Cons:
- We’ll have to set an excess addiction;
- We’ll be limited by minSdkVersion = 18;
- We’ll have to play with weird UiObjects.
Go on, what about something else?
Okay… 😏
Let’s try one hacky way using Espresso
At first we have to extend ViewInteration class by adding new methods:
- isDisplayed(): Boolean
- doesNotExist(): Boolean
- waitForAppear(millis: Long): ViewInteraction
- waitForDisappear(millis: Long): ViewInteraction
fun ViewInteraction.isDisplayed(): Boolean {
try {
this.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
} catch (ignored: NoMatchingViewException) {
return false
}
return true
}
fun ViewInteraction.doesNotExist(): Boolean {
try {
this.check(ViewAssertions.doesNotExist())
} catch (ignored: AssertionError) {
return false
}
return true
}
fun ViewInteraction.waitForAppear(millis: Long = 5000): ViewInteraction {
val timeout = System.currentTimeMillis() + millis
while (System.currentTimeMillis() < timeout) {
if (this.isDisplayed()) { break }
}
return this
}
fun ViewInteraction.waitForDisappear(millis: Long = 5000): ViewInteraction {
val timeout = System.currentTimeMillis() + millis
while (System.currentTimeMillis() < timeout) {
if (this.doesNotExist()) { break }
}
return this
}
Then we can try to use it in the tests:
@Test
fun whenViewWillAppear() {
val viewMatcher = ViewMatchers.withId(R.id.sample_view_id)
Espresso.onView(viewMatcher)
.waitForAppear()
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
}
@Test
fun whenViewWillDisappear() {
val viewMatcher = ViewMatchers.withId(R.id.sample_view_id)
Espresso.onView(viewMatcher)
.waitForDisappear()
.check(ViewAssertions.doesNotExist())
}
Pros:
- Nothing is easier than use it for all the cases;
- We’re able to control the waiting timeout for each case.
Cons:
- Logcat will be littered;
- After all, it a bit eats up the memory (comparing with Idling Resources).
Conclusion?
Yeah, waits are the delicate matter:
- Follow the deadlines, needs, requirements and sanity;
- Do whatever you want (using Espresso, of course 🧐);
- Enjoy! 😉