When testing with Espresso, setting a value on a Slider directly does not trigger the onChange
or OnSliderTouchListener
callbacks.
I haven't found any official documentation or reliable information online on how to properly simulate user interaction with a Slider in tests.
If you're using OnSliderTouchListener
, here’s a solution that works using reflection:
fun setValueAndClick(value: Int): ViewAction {
return object : ViewAction {
override fun getDescription(): String? {
return "Set value and trigger onStopTrackingTouch."
}
override fun getConstraints(): Matcher<View?>? {
return isAssignableFrom(Slider::class.java)
}
override fun perform(uiController: UiController?, view: View) {
val slider = view as Slider
slider.value = value.toFloat()
// Slider extends BaseSlider
val baseSliderClass = slider.javaClass.superclass
// BaseSlider contains "List<T> touchListeners"
val touchListenersField: Field = baseSliderClass.getDeclaredField("touchListeners")
touchListenersField.isAccessible = true
val listeners = touchListenersField.get(slider) as? List<*>
val listener = listeners?.find { it is Slider.OnSliderTouchListener }
if (listener == null) throw Exception("${Slider.OnSliderTouchListener::class.simpleName} not found.")
for (method in listener.javaClass.declaredMethods) {
if (method.name == "onStopTrackingTouch") {
method.isAccessible = true
method.invoke(listener, slider)
break
}
}
}
}
}
Usage Example:
onView(withId(R.id.set_number_slider)).perform(setValueAndClick(10))
You can call onStartTrackingTouch
simply by replacing "onStopTrackingTouch"
with it. I couldn't come up with a better way; I searched for ready-made solutions online, but they involved questionable calculations with "magic" numbers for correcting deviations. At least the reflection will work reliably.