پویانمایی‌های آزمایشی

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

بهینه‌سازی آزمایش‌های پویانمایی

هنگام آزمایش پویانمایی‌های با کیفیت بالا، اغلب لازم است جلوروی خودکار را غیرفعال کنید و به‌صورت دستی فریم‌ها را گام‌به‌گام بررسی کنید تا وضعیت‌های واسط رابط کاربری را تأیید کنید. برای این حلقه‌های فریم‌به‌فریم خاص، از روش 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)
            }
        }
    }
}