mattinger
04/23/2026, 3:09 PMinteraction.performTouchInput {
down(snapCenter)
up()
down(snapCenter)
moveTo(targetCenter)
up()
}
waitForIdle()
However, in the v2 API the difference is that the runComposeUiTest (or even just a normal createComposeRule) has switched to a StandardTestDispatcher from UnconfinedTestDispatcher. I'm able to switch to UnconfinedTestDispatcher, but that's local to one test. I actually have the logic above wrapped up in a test fixture, and if someone uses it without the right dispatcher it won't work.
Is there some other alternative other than switching the dispatcher to force the effects to execute at the point where i'm calling waitForIdleLouis Pullen-Freilich [G]
04/23/2026, 4:05 PMmattinger
04/23/2026, 4:27 PMLouis Pullen-Freilich [G]
04/23/2026, 5:19 PMLouis Pullen-Freilich [G]
04/23/2026, 5:20 PMrunCurrent manually.
https://developer.android.com/develop/ui/compose/testing/migrate-v2#manual-synchronization
There's some docs here and elsewhere on the pagemattinger
04/23/2026, 5:39 PMmattinger
04/23/2026, 5:39 PMrunComposeUiTest(effectContext = UnconfinedTestDispatcher()) {
val time = LocalTime.of(17,30)
var hour by mutableStateOf(time.hour)
var minute by mutableStateOf(time.minute)
setContent {
CLTimePicker(
modifier = Modifier.testTag("timePicker"),
onHourSelected = { hour = it },
onMinuteSelected = { minute = it},
initialHour = time.hour,
initialMinute = time.minute,
mode = CLTimePickerMode.Dial
)
}
val robot = CLTimePickerRobot(this, "timePicker")
println(robot.interaction.printToString())
robot
.assertIs24Hour()
.selectHour(7)
.selectMinute(58)
assertThat(hour).isEqualTo(7)
assertThat(minute).isEqualTo(58)
}mattinger
04/23/2026, 5:40 PMmattinger
04/23/2026, 5:41 PMLaunchedEffect(onMinuteSelected) {
snapshotFlow { state.minute }.collect {
onMinuteSelected(it)
}
}mattinger
04/23/2026, 5:42 PMmattinger
04/23/2026, 5:53 PMwaitUntil(timeoutMillis = 5000) {
minute == 58
}
But with a StandardTestDispatcher, it's never delivering the callback.mattinger
04/23/2026, 6:00 PM