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
测试高保真动画时,您通常需要停用自动推进功能,并手动逐帧检查,以断言中间界面状态。对于这些特定的逐帧循环,请使用 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
- ปัจจัยอื่นๆ ที่ควรพิจารณา
- ปรับแต่งภาพเคลื่อนไหว