I have an app with a splashscreen, which stays for about 2 seconds.
After that, it switches to another activity A
. In A
, I set a value in a SeekBar
and after that, click a Button
to confirm.
When I simply start a recorded Espresso test doing this, it tries to play while on the splashscreen. So when it tried to set the SeekBar
value or click the Button
, I get a NoMatchingViewException
. So my first attempt at fixing this was to simply add a sleep(5000)
. This worked.
However, I dont want to put a manual sleep in after every Activity switch.
- Because it seems like unnecessary code
- Because it would mean unnecessary waiting time for running the test
- The timing might be arbitrary and could be different for different devices
So I tried to check whether or not Im in the right Activity/can see the right views. I did this using some SO links: Wait for Activity and Wait for View.
However, even that does not work 100%.
I have these two functions:
fun <T: AppCompatActivity> waitForActivity(activity: Class<T>, timeout: Int = 5000, waitTime: Int = 100) {
val maxTries = timeout / waitTime
var tries = 0
for(i in 0..maxTries) {
var currentActivity: Activity? = null
getInstrumentation().runOnMainSync { run { currentActivity = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(
Stage.RESUMED).elementAtOrNull(0) } }
if(activity.isInstance(currentActivity)) {
break
} else {
tries++
sleep(waitTime.toLong())
}
}
}
fun waitForView(
@IntegerRes id: Int,
waitMillis: Int = 5000,
waitMillisPerTry: Long = 100
): ViewInteraction {
// Derive the max tries
val viewMatcher = allOf(
withId(id),
isDisplayed()
)
val maxTries = waitMillis / waitMillisPerTry.toInt()
var tries = 0
for (i in 0..maxTries)
try {
tries++
val element = onView(viewMatcher)
element.check { view, noViewFoundException ->
if(view == null) {
throw noViewFoundException ?: Exception("TEST")
}
if(view.hasWindowFocus()) {
throw noViewFoundException ?: Exception("TEST2")
}
}
return element
} catch (e: Exception) {
if (tries == maxTries) {
throw e
}
sleep(waitMillisPerTry)
}
throw Exception("Error finding a view matching $viewMatcher")
}
Neither of those work 100%. Both of them seem to return within the timeout restrictions, and have "found" the activity/view. However, the expected view, e.g. a Button
is not yet ready to perform, for example, element.perform(click())
. It does not lead to a NoMatchingViewException
, but it does not perform the click I did either. For the SeekBar
, I use this:
private fun setSeekValue(seekBar: ViewInteraction, age: Int) {
val fullPercentage = .9f
val step = 1/99f
seekBar.perform(
GeneralClickAction(
Tap.SINGLE,
CoordinatesProvider { view ->
val pos = IntArray(2)
view?.getLocationOnScreen(pos)
FloatArray(2).apply {
this[0] = pos[0] + view!!.width * (.05f + fullPercentage*step*age)
this[1] = pos[1] + view.height * .5f
}
},
PrecisionDescriber {
FloatArray(2).apply {
this[0] = .1f
this[1] = 1f
}
},
InputDevice.SOURCE_MOUSE,
MotionEvent.ACTION_BUTTON_PRESS
)
)
}
However, when I use these functions and just put a very short sleep, e.g. sleep(100)
after it, it works. This again however, would go against the three reasons listed above, which im trying to avoid here.
As you can see in the function waitForView
, I tried to check if the View is "usable", using hasWindowFocus()
. But this still does not perform the click, except for when I again put a sleep(80)
or something after it. So it waits for the splashscreen to switch to A
, finds the view it's looking for and then cant perform the click, except for when I wait a little bit.
I have also tried these functions of View
:
- isEnabled
- isShown
- visibility
- getDrawingRect
- isFocusable
- isFocused
- isLayoutDirectionResolved
Neither of them worked as I expected. With all of them, after the needed value was returned on the element.check
part of waitForView
, they would still not be accessible without putting a short sleep
after.
Is there a way to reliably check if I can perform a click on a view/safely can perform ViewInteraction.perform()
Either by checking, if an activity is fully loaded to a point where its views are usable. Or by directly checking if a view is usable.
Aucun commentaire:
Enregistrer un commentaire