Compose bietet ComposeTestRule, mit dem Sie Tests für Animationen deterministisch und mit voller Kontrolle über die Testuhr schreiben können. So können Sie Zwischenwerte der Animation prüfen. Außerdem kann ein Test schneller ausgeführt werden als die tatsächliche Dauer der Animation.
ComposeTestRule stellt seine Testuhr als mainClock bereit. Sie können das Attribut autoAdvance auf „false“ setzen, um die Uhr in Ihrem Testcode zu steuern. Nachdem Sie die Animation gestartet haben, die Sie testen möchten, können Sie die Uhr mit advanceTimeBy vorstellen.
Beachten Sie, dass advanceTimeBy die Uhr nicht genau um die angegebene Dauer verschiebt. Stattdessen wird sie auf die nächste Dauer aufgerundet, die ein Vielfaches der Frame-Dauer ist.
@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() }
Animationstests optimieren
Beim Testen von High-Fidelity-Animationen müssen Sie oft den automatischen Übergang deaktivieren und manuell durch die Frames gehen, um Zwischenzustände der Benutzeroberfläche zu prüfen. Verwenden Sie für diese spezifischen Frame-für-Frame-Schleifen die Methode runWithoutImplicitWait, um Ihre Assertions auszuführen. Standardknotenabfragen (wie onNodeWithTag oder fetchSemanticsNode) lösen implizite Synchronisierungen aus, die redundant sind, wenn Sie die Uhr manuell steuern. Wenn Sie sie umgehen, werden die Testlaufzeiten erheblich beschleunigt.
Nutzungsrichtlinien
- Manuelle Taktverwaltung: Verwenden Sie diese API, wenn
mainClock.autoAdvanceauffalsegesetzt ist und sich die Benutzeroberfläche für den aktuellen Frame in einem bekannten, stabilen Zustand befindet. - Ausführung im UI-Thread: Um die Stabilität des UI-Baums zu gewährleisten, rufen Sie
runWithoutImplicitWaitim UI-Thread auf, z. B. mitrunOnUiThread. Wenn Sie ihn außerhalb des UI-Threads ausführen, ist Ihr Test Race-Bedingungen und veralteten Statuslesevorgängen ausgesetzt. - Schreibgeschützte Assertions: Der Block darf nur schreibgeschützte Assertions enthalten. Alle Aktionen, die den Status ändern, sollten außerhalb dieses Blocks ausgeführt werden.
Beispiel
@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) } } } }
Empfehlungen für Sie
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Compose-Layout testen
- Weitere Überlegungen
- Animationen anpassen