Compose ofrece ComposeTestRule, que te permite escribir pruebas para animaciones de manera determinista con control total sobre el reloj de prueba. Esto te permite verificar los valores de animación intermedios. Además, una prueba puede ejecutarse más rápido que la duración real de la animación.
ComposeTestRule expone su reloj de prueba como mainClock. Puedes configurar la propiedad autoAdvance como "false" para controlar el reloj en tu código de prueba. Después de iniciar la animación que deseas probar, el reloj puede moverse con advanceTimeBy.
Debes tener en cuenta que advanceTimeBy no mueve el reloj exactamente según la duración especificada. En cambio, se redondea a la duración más cercana que sea multiplicador de la duración del fotograma.
@get:Rule val rule = createComposeRule() @Test fun testAnimationWithClock() { // Pause animations rule.mainClock.autoAdvance = false var enabled by mutableStateOf(false) rule.setContent { val color by animateColorAsState( targetValue = if (enabled) Color.Red else Color.Green, animationSpec = tween(durationMillis = 250) ) Box(Modifier.size(64.dp).background(color)) } // Initiate the animation. enabled = true // Let the animation proceed. rule.mainClock.advanceTimeBy(50L) // Compare the result with the image showing the expected result. // `assertAgainGolden` needs to be implemented in your code. rule.onRoot().captureToImage().assertAgainstGolden() }
Optimiza las pruebas de animación
Cuando pruebas animaciones de alta fidelidad, a menudo necesitas inhabilitar el avance automático y recorrer los fotogramas de forma manual para confirmar los estados intermedios de la IU. Para estos
bucles específicos fotograma por fotograma, usa el runWithoutImplicitWait método para
ejecutar tus aserciones. Las consultas de nodos estándar (como onNodeWithTag o
fetchSemanticsNode) activan sincronizaciones implícitas que son redundantes
cuando controlas el reloj de forma manual, por lo que omitirlas acelera significativamente
los tiempos de ejecución de las pruebas.
lineamientos de uso
- Administración manual del reloj: Usa esta API cuando
mainClock.autoAdvanceesté configurado comofalsey la IU se encuentre en un estado estable y conocido para el fotograma actual. - Ejecución del subproceso de IU: Para garantizar la estabilidad del árbol de la IU, llama a
runWithoutImplicitWaiten el subproceso de IU, como conrunOnUiThread. Si lo ejecutas fuera del subproceso de IU, la prueba estará expuesta a condiciones de carrera y lecturas de estado obsoletas. - Aserciones de solo lectura: El bloque debe contener estrictamente aserciones de solo lectura. Cualquier acción que modifique el estado debe realizarse fuera de este bloque.
Ejemplo
@Test fun runWithoutImplicitWaitSample() = runComposeUiTest { setContent { MainScreen() } mainClock.autoAdvance = false // Trigger an animation onNodeWithText("Start Animation").performClick() // Step through the animation frame-by-frame while (hasPendingWork()) { mainClock.advanceTimeByFrame() waitForIdle() runOnUiThread { // Suppress implicit synchronization inside this block to avoid redundant // waits on each node query, making the frame assertions execute much faster. runWithoutImplicitWait { val box1 = onNodeWithTag("Box1").fetchSemanticsNode() val box2 = onNodeWithTag("Box2").fetchSemanticsNode() val box3 = onNodeWithTag("Box3").fetchSemanticsNode() // Assert the exact intermediate state of all three properties for this frame assert(box1.boundsInRoot.right <= box2.boundsInRoot.left) assert(box2.boundsInRoot.right <= box3.boundsInRoot.left) } } } }
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Cómo probar tu diseño de Compose
- Otras consideraciones
- Cómo personalizar animaciones