Büyük test kararlılığı

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 vermeyi bitirmesini 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 değilse geçersiz olur.

Compose veya Espresso gibi modern çerçeveler, test göz önünde bulundurularak tasarlandığından, sonraki test işlemi veya beyanı öncesinde 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 geçişi yapmadan önce uygulamanın boşta olup olmadığını kontrol eden bir döngüyü gösteren akış şeması
Şekil 1: Test senkronizasyonu.

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. coroutine'ler için TestDispatcher veya RxJava için RxIdler).

Senkronizasyon sabit bir süre beklemeye dayalı olduğunda testin başarısız olduğunu gösteren diyagram
Şekil 2: Testlerde uyku modunun kullanılması, yavaş veya kararsız testlere yol açar.

Kararlılığı artırmanın 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ü testlerini kullanırsınız.

UI Otomasyon Aracı gibi diğer çerçeveler, sistem kullanıcı arayüzü ve diğer uygulamalarla etkileşim kurabileceğiniz için daha geniş bir kapsam sunar. Ancak kullanıcı arayüzü otomasyon testleri daha fazla manuel senkronizasyon gerektirebileceğinden daha az güvenilir olma eğilimindedir.

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 işlem yapan bileşenler, kullanıcı arayüzü hazır olmadan önce bir test ifadesi yürütüldüğü için test hatalarına neden olabilir. Testin kapsamı genişledikçe testin kararsız olma olasılığı da 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 asenkron işlemi izlemek zordur. Ayrıca, test edilen kodu kirletmeden boşta kalma kaynaklarını yüklemek 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.

Bekle-ta ki mekanizması, devam etmeden önce bir koşulun karşılanıp karşılanmadığını sorarak çalışır.
Şekil 3. Koşulların karşılanması için beklemek kararsızlığı azaltır.

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)

Ayrıca, doğru veya yanlış değer döndüren tüm işlevleri alan genel bir API:

fun waitUntil(timeoutMillis: Long, condition: () -> Boolean): Unit

Örnek kullanım:

composeTestRule.waitUntilExactlyOneExists(hasText("Continue")</code>)</p></td>

Yeniden deneme mekanizmaları

Hata veren testleri düzeltmeniz gerekir ancak bazen testlerin başarısız olmasına neden olan koşullar o kadar olası değildir ki bu koşulları yeniden oluşturmak zordur. Her zaman kararsız testleri takip edip düzeltmeniz gerekir. Ancak, test başarılı olana kadar birkaç kez çalıştırılarak geliştirici üretkenliğini korumaya yardımcı olabilecek bir yeniden deneme mekanizması kullanabilirsiniz.

Aşağıdakiler gibi sorunların önlenmesi için birden fazla düzeyde yeniden deneme yapılması gerekir:

  • Cihazla bağlantı zaman aşımına uğradı veya bağlantı kesildi
  • Tek bir 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 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.