Mobil uygulamaların ve çerçevelerin eşzamansız yapısı, güvenilir ve tekrarlanabilir testler yazmayı çoğu zaman zorlaştırır. Bir kullanıcı etkinliği eklendiğinde test çerçevesi, uygulamanın buna tepki vermesinin tamamlanmasını beklemelidir. Bu işlem, ekrandaki bazı metinlerin değiştirilmesinden bir etkinliğin tamamen yeniden oluşturulmasına kadar değişebilir. Bir testin davranışı kesin olmadığında test geçersiz olur.
Compose veya Espresso gibi modern çerçeveler, test göz önünde bulundurularak tasarlandığından, bir sonraki test işlemi veya beyanından önce kullanıcı arayüzünün boşta olacağından emin olabilirsiniz. Buna senkronizasyon denir.
Test senkronizasyonu
Testin bilmediği, örneğin bir veritabanından veri yükleme veya sonsuz animasyon gösterme gibi asenkron veya arka plan işlemleri çalıştırdığınızda da sorunlar ortaya çıkabilir.
Test paketinizin güvenilirliğini artırmak için Espresso Idling Resources gibi arka plan işlemlerini izlemenin bir yolunu yükleyebilirsiniz. Ayrıca, modülleri, boşta kalma durumu için sorgulayabileceğiniz veya senkronizasyonu iyileştiren test sürümleriyle değiştirebilirsiniz (ör. iş parçacığı için TestDispatcher veya RxJava için RxIdler).
Kararlılığı iyileştirme yolları
Büyük testler, bir uygulamanın birden fazla bileşenini test ettiğinden aynı anda çok sayıda gerileme yakalayabilir. Genellikle emülatörlerde veya cihazlarda çalıştırılırlar. Bu da yüksek doğruluk oranına sahip oldukları anlamına gelir. Büyük uçtan uca testler kapsamlı bir kapsam sunsa da zaman zaman hatalara daha yatkındır.
Kararsızlığı azaltmak için uygulayabileceğiniz başlıca önlemler şunlardır:
- Cihazları doğru şekilde yapılandırma
- Senkronizasyon sorunlarını önleme
- Yeniden denemeleri uygulama
Compose veya Espresso'yu kullanarak büyük testler oluşturmak için genellikle etkinliklerinizden birini başlatır ve kullanıcı gibi gezinerek kullanıcı arayüzünün doğru şekilde davrandığını doğrularsınız. Bu işlem için de iddialar veya ekran görüntüsü testleri kullanırsınız.
UI Automator gibi diğer çerçeveler, sistem kullanıcı arayüzü ve diğer uygulamalarla etkileşim kurabileceğiniz için daha geniş bir kapsama olanak tanır. Bununla birlikte, UI Automator testleri daha fazla manuel senkronizasyon gerektirebilir. Bu nedenle, genellikle daha az güvenilirdirler.
Cihazları yapılandırma
Öncelikle, testlerinizin güvenilirliğini artırmak için cihazın işletim sisteminin testlerin yürütülmesini beklenmedik bir şekilde kesintiye uğratmadığından emin olmanız gerekir. Örneğin, diğer uygulamaların üzerinde bir sistem güncelleme iletişim kutusu gösterildiğinde veya diskteki alan yetersiz olduğunda.
Cihaz çiftliği sağlayıcıları, cihazlarını ve emülatörlerini yapılandırır. Bu nedenle, genellikle herhangi bir işlem yapmanız gerekmez. Ancak özel durumlar için kendi yapılandırma yönergeleri olabilir.
Gradle tarafından yönetilen cihazlar
Emülatörleri kendiniz yönetiyorsanız testlerinizi çalıştırmak için hangi cihazların kullanılacağını tanımlamak üzere Gradle tarafından yönetilen cihazları kullanabilirsiniz:
android {
testOptions {
managedDevices {
localDevices {
create("pixel2api30") {
// Use device profiles you typically see in Android Studio.
device = "Pixel 2"
// Use only API levels 27 and higher.
apiLevel = 30
// To include Google services, use "google".
systemImageSource = "aosp"
}
}
}
}
}
Bu yapılandırmayla aşağıdaki komut bir emülatör görüntüsü oluşturur, bir örnek başlatır, testleri çalıştırır ve örneği kapatır.
./gradlew pixel2api30DebugAndroidTest
Gradle tarafından yönetilen cihazlar, cihaz bağlantısının kesilmesi ve diğer iyileştirmeler durumunda yeniden deneme mekanizmaları içerir.
Senkronizasyon sorunlarını önleme
Arka planda veya eşzamansız olarak işlem yapan bileşenler, kullanıcı arayüzü hazır olmadan önce bir test ifadesi çalıştırıldığı için test hatalarına neden olabilir. Bir testin kapsamı genişledikçe eksik kalma ihtimali artar. Test çerçevelerinin bir etkinliğin yüklemenin tamamlanıp tamamlanmadığını veya daha uzun süre beklemesi gerekip gerekmediğini çıkarması gerektiğinden, bu senkronizasyon sorunları tutarsızlıkların birincil kaynağıdır.
Çözümler
Bir uygulamanın ne zaman meşgul olduğunu belirtmek için Espresso'nun boşta kalma kaynaklarını kullanabilirsiniz, ancak özellikle çok büyük uçtan uca testlerde her eşzamansız işlemi izlemek zordur. Ayrıca boşta kalan kaynakların, test edilen kodu kirletmeden yüklenmesi zor olabilir.
Bir etkinliğin meşgul olup olmadığını tahmin etmek yerine, testlerinizi belirli koşullar karşılanana kadar bekletebilirsiniz. Örneğin, kullanıcı arayüzünde belirli bir metin veya bileşen gösterilene kadar bekleyebilirsiniz.
Compose, farklı eşleştiricileri beklemek için ComposeTestRule
kapsamında bir dizi test API'sine sahiptir:
fun waitUntilAtLeastOneExists(matcher: SemanticsMatcher, timeout: Long = 1000L)
fun waitUntilDoesNotExist(matcher: SemanticsMatcher, timeout: Long = 1000L)
fun waitUntilExactlyOneExists(matcher: SemanticsMatcher, timeout: Long = 1000L)
fun waitUntilNodeCount(matcher: SemanticsMatcher, count: Int, timeout: Long = 1000L)
Boole değeri döndüren herhangi bir işlevi alan genel bir API:
fun waitUntil(timeoutMillis: Long, condition: () -> Boolean): Unit
Örnek kullanım:
composeTestRule.waitUntilExactlyOneExists(hasText("Continue")</code>)</p></td>
Yeniden deneme mekanizmaları
Güvenilir olmayan testleri düzeltmeniz gerekir ancak bazen bunların başarısız olmasına yol açan koşullar çok zor olabilir ve bunları yeniden üretmek zor olabilir. Her zaman kararsız testleri takip edip düzeltmeniz gerekir. Ancak yeniden deneme mekanizması, test başarılı olana kadar birkaç kez çalıştırılarak geliştirici üretkenliğini korumaya yardımcı olabilir.
Aşağıdaki gibi sorunları önlemek için yeniden denemelerin birden fazla düzeyde yapılması gerekir:
- Cihaz bağlantısı zaman aşımına uğradı veya bağlantı kesildi
- Tek test hatası
Yeniden denemelerin yüklenmesi veya yapılandırılması, test çerçevelerinize ve altyapınıza bağlıdır ancak tipik mekanizmalar şunlardır:
- Herhangi bir testi birkaç kez yeniden deneyen bir JUnit kuralı
- CI iş akışınızdaki bir yeniden deneme işlemi veya adım
- Gradle tarafından yönetilen cihazlar gibi yanıt vermeyen bir emülatörü yeniden başlatan bir sistem.