Compose offre ComposeTestRule, che ti consente di scrivere test per le animazioni in modo deterministico con il controllo completo sull'orologio di test. In questo modo puoi verificare i valori di animazione intermedi. Inoltre, un test può essere eseguito più velocemente della durata effettiva dell'animazione.
ComposeTestRule espone il proprio clock di test come mainClock. Puoi impostare la proprietà autoAdvance su false per controllare l'orologio nel codice di test. Dopo aver avviato l'animazione che vuoi testare, l'orologio può essere spostato in avanti con advanceTimeBy.
Tieni presente che advanceTimeBy non sposta l'orologio esattamente della durata specificata. ma la arrotonda per eccesso alla durata più vicina che è un multiplo della durata del frame.
@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() }
Test di animazioni Optimize
Quando testi animazioni ad alta fedeltà, spesso devi disattivare l'avanzamento automatico
e scorrere manualmente i frame per verificare gli stati intermedi della UI. Per questi loop frame per frame specifici, utilizza il metodo runWithoutImplicitWait per eseguire le asserzioni. Le query standard sui nodi (come onNodeWithTag o fetchSemanticsNode) attivano sincronizzazioni implicite che sono ridondanti quando controlli manualmente l'orologio, quindi ignorarle accelera notevolmente i tempi di esecuzione dei test.
Linee guida sull'utilizzo
- Gestione manuale dell'orologio: utilizza questa API quando
mainClock.autoAdvanceè impostato sufalsee l'interfaccia utente è in uno stato noto e stabile per il frame corrente. - Esecuzione del thread dell'interfaccia utente: per garantire la stabilità dell'albero dell'interfaccia utente, chiama
runWithoutImplicitWaitsul thread dell'interfaccia utente, ad esempio conrunOnUiThread. L'esecuzione al di fuori del thread dell'interfaccia utente espone il test a race condition e letture di stato obsolete. - Asserzioni di sola lettura: il blocco deve contenere rigorosamente asserzioni di sola lettura. Tutte le azioni che modificano lo stato devono essere eseguite al di fuori di questo blocco.
Esempio
@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) } } } }
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Test del layout di Compose
- Altre considerazioni
- Personalizza le animazioni