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 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

Test tarafından bilinmeyen, asenkron veya arka plan işlemleri (ör. veritabanından veri yükleme veya sonsuz animasyon gösterme) ç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. iş parçacığı 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 şema
Şekil 2: Testlerde uyku modunun kullanılması, testlerin yavaş veya kararsız olmasına neden olur.

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ü testleri kullanırsınız.

UI Otomasyonu 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 olarak 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.

Compose, farklı eşleştiricilerin gelmesini 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 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.