Animasyonları test etme

Compose, test saati üzerinde tam kontrol sahibi olarak animasyonlar için deterministik bir şekilde testler yazmanıza olanak tanıyan ComposeTestRule sunar. Bu sayede ara animasyon değerlerini doğrulayabilirsiniz. Ayrıca test, animasyonun gerçek süresinden daha hızlı çalışabilir.

ComposeTestRule, test saatini mainClock olarak gösterir. Test kodunuzda saati kontrol etmek için autoAdvance özelliğini "false" olarak ayarlayabilirsiniz. Test etmek istediğiniz animasyonu başlattıktan sonra saat advanceTimeBy ile ileri alınabilir.

Burada dikkat edilmesi gereken nokta, advanceTimeBy işlevinin saati tam olarak belirtilen süre kadar ileri almamasıdır. Bunun yerine, kare süresinin katı olan en yakın süreye yuvarlar.

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

Animasyon testlerini optimize etme

Yüksek kaliteli animasyonları test ederken genellikle otomatik ilerlemeyi devre dışı bırakmanız ve ara kullanıcı arayüzü durumlarını onaylamak için kareleri manuel olarak ilerletmeniz gerekir. Bu kare kare döngüler için runWithoutImplicitWait yöntemini kullanarak onaylarınızı yürütün. Standart düğüm sorguları (onNodeWithTag veya fetchSemanticsNode gibi), saati manuel olarak kontrol ettiğinizde gereksiz olan dolaylı senkronizasyonları tetikler. Bu nedenle, bunları atlamak test çalışma sürelerinizi önemli ölçüde hızlandırır.

Kullanım talimatları

  • Manuel saat yönetimi: mainClock.autoAdvance, false olarak ayarlandığında ve kullanıcı arayüzü mevcut kare için bilinen, kararlı bir durumdayken bu API'yi kullanın.
  • Kullanıcı arayüzü iş parçacığı yürütme: Kullanıcı arayüzü ağacının kararlılığını sağlamak için kullanıcı arayüzü iş parçacığında runWithoutImplicitWait yöntemini çağırın (ör. runOnUiThread ile). Testi kullanıcı arayüzü iş parçacığının dışında çalıştırmak, testinizi yarışma durumlarına ve eski durum okumalarına maruz bırakır.
  • Salt okunur onaylar: Blok, kesinlikle salt okunur onaylar içermelidir. Durumu değiştiren tüm işlemler bu bloğun dışında gerçekleştirilmelidir.

Örnek

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