Compose offre ComposeTestRule che ti consente di scrivere test per le animazioni
in modo deterministico con il pieno controllo dell'orologio di test. In questo modo puoi
verificare i valori di animazione intermedi. Inoltre, un test può essere eseguito più rapidamente
della durata effettiva dell'animazione.
ComposeTestRule espone il suo orologio 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 lo arrotonda per eccesso alla durata più vicina che sia
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() }
Ottimizzare i test delle animazioni
Quando testi animazioni ad alta fedeltà, spesso devi disattivare l'avanzamento automatico e scorrere manualmente i frame per verificare gli stati intermedi dell'UI. Per questi
loop frame per frame specifici, utilizza il runWithoutImplicitWait metodo per
eseguire le asserzioni. Le query sui nodi standard (come onNodeWithTag o
fetchSemanticsNode) attivano sincronizzazioni implicite ridondanti
quando controlli manualmente l'orologio, quindi ignorarle velocizza notevolmente i tempi di esecuzione dei test.
Linee guida sull'utilizzo
- Gestione manuale dell'orologio: utilizza questa API quando
mainClock.autoAdvanceè impostato sufalsee l'UI è 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 stati non aggiornati. - Asserzioni di sola lettura: il blocco deve contenere rigorosamente asserzioni di sola lettura. Eventuali 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
- Testare il layout di Composizione
- Altre considerazioni
- Personalizzare le animazioni