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

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: Senkronizasyonu test etme.

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ığı 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.