Kiểm thử ảnh động

Compose cung cấp ComposeTestRule cho phép bạn viết các phép kiểm thử cho ảnh động theo cách thức xác định với toàn quyền kiểm soát trên đồng hồ kiểm thử. Tính năng này cho phép bạn xác minh các giá trị ảnh động trung gian. Ngoài ra, một thử nghiệm ảnh động có thể chạy nhanh hơn thời lượng thực tế của ảnh động.

ComposeTestRule cho thấy đồng hồ thử nghiệm là mainClock. Bạn có thể thiết lập thuộc tính autoAdvance thành false để kiểm soát đồng hồ trong mã thử nghiệm. Sau khi bắt đầu ảnh động mà bạn muốn thử nghiệm, đồng hồ có thể được di chuyển về phía trước bằng advanceTimeBy.

Cần lưu ý là advanceTimeBy không di chuyển đồng hồ một cách chính xác theo thời lượng đã chỉ định. Thay vào đó, hệ thống sẽ làm tròn lên thời lượng gần nhất bằng hệ số thời gian kết xuất khung hình.

@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()
}

Tối ưu hoá các thử nghiệm ảnh động

Khi kiểm thử ảnh động có độ trung thực cao, bạn thường cần tắt tính năng tự động chuyển tiếp và chuyển qua các khung hình theo cách thủ công để xác nhận các trạng thái giao diện người dùng trung gian. Đối với các vòng lặp cụ thể từng khung hình này, hãy sử dụng phương thức runWithoutImplicitWait để thực thi các câu khẳng định của bạn. Các truy vấn nút tiêu chuẩn (như onNodeWithTag hoặc fetchSemanticsNode) sẽ kích hoạt các hoạt động đồng bộ hoá ngầm không cần thiết khi bạn đang kiểm soát đồng hồ theo cách thủ công. Do đó, việc bỏ qua các truy vấn này sẽ tăng đáng kể thời gian chạy thử nghiệm.

Nguyên tắc sử dụng

  • Quản lý đồng hồ theo cách thủ công: Sử dụng API này khi mainClock.autoAdvance được đặt thành false và giao diện người dùng ở trạng thái ổn định, đã biết cho khung hình hiện tại.
  • Thực thi luồng giao diện người dùng: Để đảm bảo tính ổn định của cây giao diện người dùng, hãy gọi runWithoutImplicitWait trên luồng giao diện người dùng, chẳng hạn như với runOnUiThread. Việc chạy quy trình kiểm thử này ngoài Luồng giao diện người dùng sẽ khiến quy trình kiểm thử của bạn gặp phải tình huống tương tranh và đọc trạng thái lỗi thời.
  • Các câu khẳng định chỉ đọc: Khối này chỉ được chứa các câu khẳng định chỉ đọc. Mọi thao tác làm thay đổi trạng thái đều phải được thực hiện bên ngoài khối này.

Ví dụ

@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)
            }
        }
    }
}