ב-Compose יש ComposeTestRule שמאפשר לכתוב בדיקות לאנימציות באופן דטרמיניסטי עם שליטה מלאה בשעון הבדיקה. כך אפשר לאמת ערכי אנימציה ביניים. בנוסף, בדיקה יכולה לפעול מהר יותר מהמשך בפועל של האנימציה.
ComposeTestRule חושף את שעון הבדיקה שלו בתור mainClock. אפשר להגדיר את המאפיין autoAdvance כ-false כדי לשלוט בשעון בקוד הבדיקה. אחרי שמפעילים את האנימציה שרוצים לבדוק, אפשר להזיז את השעון קדימה באמצעות advanceTimeBy.
חשוב לציין שהפונקציה advanceTimeBy לא מזיזה את השעון בדיוק לפי משך הזמן שצוין. במקום זאת, הוא מעגל את משך הזמן כלפי מעלה למשך הזמן הקרוב ביותר שהוא כפולה של משך הזמן של הפריים.
@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() }
בדיקות של Optimize animation
测试高保真动画时,您通常需要停用自动推进功能,并手动逐帧检查,以断言中间界面状态。对于这些特定的逐帧循环,请使用 runWithoutImplicitWait 方法执行断言。标准节点查询(例如 onNodeWithTag 或 fetchSemanticsNode)会触发隐式同步,当您手动控制时钟时,这些同步是多余的,因此绕过它们可以显著加快测试运行速度。
使用指南
- 手动时钟管理:当
mainClock.autoAdvance设置为false且界面处于当前帧的已知稳定状态时,请使用此 API。 - 界面线程执行:为确保界面树的稳定性,请在界面线程上调用
runWithoutImplicitWait,例如使用runOnUiThread。在界面线程之外运行它会使测试面临竞态条件和过时状态读取。 - 只读断言:该块应严格包含只读断言。任何改变状态的操作都应在此代码块之外执行。
示例
@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) } } } }
מומלץ בשבילכם
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- בדיקת הפריסה של Compose
- עוד דברים שכדאי לבדוק
- התאמה אישית של אנימציות