Compose testleri varsayılan olarak kullanıcı arayüzünüzle senkronize edilir. ComposeTestRule ile bir onaylama veya işlem çağırdığınızda, kullanıcı arayüzü ağacı boşta kalana kadar beklenerek test önceden senkronize edilir.
Normalde herhangi bir işlem yapmanız gerekmez. Ancak bilmeniz gereken bazı uç durumlar vardır.
Bir test senkronize edildiğinde, Compose uygulamanız sanal saat kullanılarak zamanda ilerletilir. Bu nedenle, Oluşturma testleri anlık olarak çalıştırılmaz ve mümkün olduğunca hızlı bir şekilde tamamlanabilir.
Ancak testlerinizi senkronize eden yöntemleri kullanmazsanız yeniden oluşturma işlemi gerçekleşmez ve kullanıcı arayüzü duraklatılmış gibi görünür.
@Test
fun counterTest() {
val myCounter = mutableStateOf(0) // State that can cause recompositions.
var lastSeenValue = 0 // Used to track recompositions.
composeTestRule.setContent {
Text(myCounter.value.toString())
lastSeenValue = myCounter.value
}
myCounter.value = 1 // The state changes, but there is no recomposition.
// Fails because nothing triggered a recomposition.
assertTrue(lastSeenValue == 1)
// Passes because the assertion triggers recomposition.
composeTestRule.onNodeWithText("1").assertExists()
}Bu koşulun yalnızca Compose hiyerarşileri için geçerli olduğunu ve uygulamanın geri kalanında geçerli olmadığını unutmayın.
Otomatik senkronizasyonu devre dışı bırakma
ComposeTestRule gibi bir yöntemle bir onaylama veya işlem çağırdığınızda
assertExists() testiniz Compose kullanıcı arayüzü ile senkronize edilir. Bazı durumlarda bu senkronizasyonu durdurup saati kendiniz kontrol etmek isteyebilirsiniz. Örneğin, kullanıcı arayüzünün hâlâ meşgul olduğu bir noktada animasyonun doğru ekran görüntülerini almak için zamanı kontrol edebilirsiniz. Otomatik senkronizasyonu devre dışı bırakmak için autoAdvance özelliğini mainClock içinde false olarak ayarlayın:
composeTestRule.mainClock.autoAdvance = false
Genellikle bu durumda saati kendiniz ilerletirsiniz. advanceTimeByFrame() ile tam olarak bir kare veya advanceTimeBy() ile belirli bir süre ileri gidebilirsiniz:
composeTestRule.mainClock.advanceTimeByFrame()
composeTestRule.mainClock.advanceTimeBy(milliseconds)
Boşta kaynaklar
Compose, testleri ve kullanıcı arayüzünü senkronize ederek her işlemin ve onaylamanın boşta durumda yapılmasını sağlar. Bu sırada gerektiğinde saat beklenir veya ilerletilir. Ancak sonuçları kullanıcı arayüzü durumunu etkileyen bazı eşzamansız işlemler, test bunlardan haberdar olmadan arka planda çalıştırılabilir.
Testinizde bu boşta kalma kaynaklarını oluşturup kaydedin. Böylece, test edilen uygulamanın meşgul olup olmadığına veya boşta olup olmadığına karar verilirken bu kaynaklar dikkate alınır. Espresso veya Compose ile senkronize edilmeyen bir arka plan işi çalıştırıyorsanız gibi ek boşta kalma kaynakları kaydetmeniz gerekmediği sürece herhangi bir işlem yapmanız gerekmez.
Bu API, test edilen öğenin boşta mı yoksa meşgul mü olduğunu belirtmek için Espresso'nun Idling Resources'ına çok benzer. IdlingResource uygulamasını kaydetmek için Oluşturma testi kuralını kullanın.
composeTestRule.registerIdlingResource(idlingResource)
composeTestRule.unregisterIdlingResource(idlingResource)
Manuel senkronizasyon
Bazı durumlarda, Compose kullanıcı arayüzünü testinizin diğer kısımlarıyla veya test ettiğiniz uygulamayla senkronize etmeniz gerekir.
waitForIdle() işlevi, Compose'un boşta kalmasını bekler ancak işlev, autoAdvance özelliğine bağlıdır:
composeTestRule.mainClock.autoAdvance = true // Default
composeTestRule.waitForIdle() // Advances the clock until Compose is idle.
composeTestRule.mainClock.autoAdvance = false
composeTestRule.waitForIdle() // Only waits for idling resources to become idle.
Her iki durumda da waitForIdle(), bekleyen çizim ve düzen geçişlerini bekler.
Ayrıca, advanceTimeUntil() ile belirli bir koşul karşılanana kadar saati ileri sarabilirsiniz.
composeTestRule.mainClock.advanceTimeUntil(timeoutMs) { condition }
Belirtilen koşulun, bu saatten etkilenebilecek durumu kontrol etmesi gerektiğini unutmayın (yalnızca Compose durumuyla çalışır).
Animasyon testlerini optimize etme
When testing high-fidelity animations, you often need to disable auto-advance
and manually step through frames to assert intermediate UI states. For these
specific frame-by-frame loops, use the runWithoutImplicitWait method to
execute your assertions. Standard node queries (like onNodeWithTag or
fetchSemanticsNode) trigger implicit synchronizations that are redundant
when you are manually controlling the clock, so bypassing them significantly
speeds up your test runtimes.
Usage guidelines
- Manual clock management: Use this API when
mainClock.autoAdvanceis set tofalseand the UI is in a known, stable state for the current frame. - UI thread execution: To ensure the stability of the UI tree, call
runWithoutImplicitWaiton the UI thread, such as withrunOnUiThread. Running it off the UI thread exposes your test to race conditions and stale state reads. - Read-only assertions: The block should strictly contain read-only assertions. Any actions that mutate state should be performed outside of this block.
Example
@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) } } } }
Ana iş parçacığı senkronizasyonu
Compose testi artık ana iş parçacığı senkronizasyonunu destekliyor. Bu sayede waitForIdle'yı ve dolayısıyla Compose kullanıcı arayüzü işlemlerini ve onaylarını doğrudan ana iş parçacığından güvenli bir şekilde çağırabilirsiniz.
Daha önce, Compose testi kesinlikle iki iş parçacıklı bir modeli zorunlu kılıyordu: Test yürütme, arka plandaki bir test iş parçacığında gerçekleşirken kullanıcı arayüzü güncellemeleri ana iş parçacığında yapılıyordu. Ana iş parçacığından (örneğin, bir runOnUiThread bloğunun içinde) waitForIdle veya runOnIdle gibi senkronizasyon yöntemlerinin çağrılması, çerçeve ana iş parçacığı senkronizasyonunu önlemek için katı iş parçacığı kontrolleri uyguladığından IllegalStateException hatası verir.
Ana iş parçacığı senkronizasyonu etkinleştirildiğinde, Compose test çerçevesi artık ana iş parçacığında engelleme çağrıları yapıldığında bile saati ilerletebilir ve bekleyen işleri işleyebilir.
Ana iş parçacığı senkronizasyonu ne zaman kullanılır?
Testleri arka plan iş parçacığında tutmak, saf Compose testleri için standart olmaya devam etse de belirli senaryolarda ana iş parçacığı senkronizasyonu oldukça avantajlıdır:
- Karmaşık görünüm birlikte çalışabilirliği: Hem Compose hem de eski Android görünümlerini içeren karma kullanıcı arayüzlerini test ederken görünümleri değiştirmek genellikle ana iş parçacığında çalıştırmayı gerektirir. Artık iş parçacığı bağlamlarını sürekli değiştirmeden Görünümlerle etkileşime geçebilir ve Compose düğümlerinde sırayla onaylama yapabilirsiniz.
- Senkron durum değişiklikleri: Mimarınız kesinlikle ana iş parçacığına bağlı durum tutuculara dayanıyorsa artık durumu değiştirebilir ve ana iş parçacığından ayrılmadan Compose kullanıcı arayüzünün yerleşmesini hemen bekleyebilirsiniz.
- Özel test çalıştırıcılar: Özel test altyapısı oluşturuyorsanız veya test çalıştırıcının doğal olarak ana iş parçacığında yürütüldüğü ortamlar kullanıyorsanız Compose testleri artık arka plan iş parçacığı delegasyonu gerektirmeden sorunsuz bir şekilde yürütülüyor.
Örnek
Geçmişte, ana iş parçacığında senkronizasyon kesinlikle yasaklandığı için geliştiricilerin arka plan test çalıştırıcı iş parçacığı ile kullanıcı arayüzü iş parçacığı arasında gidip gelmesi gerekiyordu. Bu durum, birbirinden bağımsız testlere yol açıyordu:
@Test fun testBidirectionalInteropUIUpdates_old() { val scenario = launchFragmentInContainer<InteropFragment>() composeTestRule.waitForIdle() scenario.onFragment { fragment -> fragment.legacyButton.performClick() } // Jump to Test Thread to verify state settles inside compose composeTestRule.waitForIdle() composeTestRule.onNodeWithText("Legacy Clicks: 1").assertIsDisplayed() composeTestRule.onNodeWithText("Increment Legacy TextView").performClick() composeTestRule.waitForIdle() // Jump back to Main Thread to verify target view state settles scenario.onFragment { fragment -> assert(fragment.legacyTextView.text.toString() == "Compose Clicks: 1") } }
Ana iş parçacığı senkronizasyonu etkinleştirildiğinde, Compose ve View hiyerarşileriyle ilgili onaylamalar aynı blokta yürütülebilir:
@Test fun testBidirectionalInteropUIUpdates_new() { val scenario = launchFragmentInContainer<InteropFragment>() composeTestRule.waitForIdle() scenario.onFragment { fragment -> fragment.legacyButton.performClick() composeTestRule.waitForIdle() composeTestRule.onNodeWithText("Legacy Clicks: 1").assertIsDisplayed() composeTestRule.onNodeWithText("Increment Legacy TextView").performClick() composeTestRule.waitForIdle() assert(fragment.legacyTextView.text.toString() == "Compose Clicks: 1") } }
Koşulları bekle
Veri yükleme veya Android'in ölçme ya da çizme (yani Compose'un dışında ölçme veya çizme) gibi harici çalışmaya bağlı olan tüm koşullarda waitUntil() gibi daha genel bir kavram kullanılmalıdır:
composeTestRule.waitUntil(timeoutMs) { condition }
Ayrıca, waitUntil yardımcılarından herhangi birini de kullanabilirsiniz:
composeTestRule.waitUntilAtLeastOneExists(matcher, timeoutMs)
composeTestRule.waitUntilDoesNotExist(matcher, timeoutMs)
composeTestRule.waitUntilExactlyOneExists(matcher, timeoutMs)
composeTestRule.waitUntilNodeCount(matcher, count, timeoutMs)
Ek Kaynaklar
- Android'de uygulamaları test etme: Android testleriyle ilgili ana açılış sayfası, testlerle ilgili temel bilgiler ve teknikler hakkında daha geniş bir bakış açısı sunar.
- Testin temelleri: Android uygulaması test etmenin temel kavramları hakkında daha fazla bilgi edinin.
- Yerel testler: Bazı testleri kendi iş istasyonunuzda yerel olarak çalıştırabilirsiniz.
- Araçlı testler: Araçlı testler de çalıştırmak iyi bir uygulamadır. Yani doğrudan cihaz üzerinde çalışan testler.
- Sürekli entegrasyon: Sürekli entegrasyon, testlerinizi dağıtım ardışık düzeninize entegre etmenize olanak tanır.
- Farklı ekran boyutlarını test edin: Kullanıcıların erişebileceği çok sayıda cihaz olduğundan farklı ekran boyutlarını test etmeniz gerekir.
- Espresso: Görüntüleme tabanlı kullanıcı arayüzleri için tasarlanmış olsa da Espresso bilgisi, Compose testinin bazı yönlerinde faydalı olabilir.