Stabilitas pengujian besar

Sifat asinkron aplikasi dan framework seluler sering kali membuat sulit untuk menulis pengujian yang andal dan dapat diulang. Saat peristiwa pengguna dimasukkan, framework pengujian harus menunggu aplikasi selesai bereaksi terhadapnya, yang dapat berkisar dari mengubah beberapa teks di layar hingga pembuatan ulang aktivitas yang lengkap. Jika tidak memiliki perilaku deterministik, pengujian akan tidak stabil.

Framework modern seperti Compose atau Espresso dirancang dengan mempertimbangkan pengujian sehingga ada jaminan tertentu bahwa UI akan tidak ada aktivitas sebelum tindakan atau pernyataan pengujian berikutnya. Ini adalah sinkronisasi.

Menguji sinkronisasi

Masalah masih dapat muncul saat Anda menjalankan operasi latar belakang atau asinkron yang tidak diketahui pengujian, seperti memuat data dari database atau menampilkan animasi tanpa batas.

diagram alur yang menunjukkan loop yang memeriksa apakah aplikasi tidak ada aktivitas sebelum membuat pengujian lulus
Gambar 1: Uji sinkronisasi.

Untuk meningkatkan keandalan rangkaian pengujian, Anda dapat menginstal cara untuk melacak operasi latar belakang, seperti Resource Nonaktif Espresso. Selain itu, Anda dapat mengganti modul untuk versi pengujian yang dapat Anda kueri untuk mengetahui waktu tidak ada aktivitas atau yang meningkatkan sinkronisasi, seperti TestDispatcher untuk coroutine atau RxIdler untuk RxJava.

Diagram yang menunjukkan kegagalan pengujian saat sinkronisasi didasarkan pada menunggu waktu tetap
Gambar 2: Menggunakan tidur dalam pengujian menyebabkan pengujian yang lambat atau tidak stabil.

Cara meningkatkan stabilitas

Pengujian besar dapat mendeteksi banyak regresi secara bersamaan karena menguji beberapa komponen aplikasi. Pengujian besar biasanya berjalan di emulator atau perangkat, yang berarti memiliki fidelitas tinggi. Meskipun pengujian menyeluruh yang besar memberikan cakupan yang komprehensif, pengujian tersebut lebih rentan terhadap kegagalan sesekali.

Langkah utama yang dapat Anda lakukan untuk mengurangi ketidakstabilan adalah sebagai berikut:

  • Mengonfigurasi perangkat dengan benar
  • Mencegah masalah sinkronisasi
  • Mengimplementasikan percobaan ulang

Untuk membuat pengujian besar menggunakan Compose atau Espresso, Anda biasanya memulai salah satu aktivitas dan menavigasi seperti yang dilakukan pengguna, memverifikasi bahwa UI berperilaku dengan benar menggunakan pernyataan atau pengujian screenshot.

Framework lain, seperti UI Automator, memungkinkan cakupan yang lebih besar, karena Anda dapat berinteraksi dengan UI sistem dan aplikasi lainnya. Namun, pengujian UI Automator mungkin memerlukan lebih banyak sinkronisasi manual sehingga cenderung kurang andal.

Mengonfigurasi perangkat

Pertama, untuk meningkatkan keandalan pengujian, Anda harus memastikan bahwa sistem operasi perangkat tidak mengganggu eksekusi pengujian secara tidak terduga. Misalnya, saat dialog update sistem ditampilkan di atas aplikasi lain atau saat ruang di disk tidak memadai.

Penyedia farm perangkat mengonfigurasi perangkat dan emulator mereka sehingga biasanya Anda tidak perlu melakukan tindakan apa pun. Namun, mereka mungkin memiliki perintah konfigurasi mereka sendiri untuk kasus khusus.

Perangkat yang dikelola Gradle

Jika Anda mengelola emulator sendiri, Anda dapat menggunakan perangkat yang dikelola Gradle untuk menentukan perangkat yang akan digunakan untuk menjalankan pengujian:

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

Dengan konfigurasi ini, perintah berikut akan membuat image emulator, memulai instance, menjalankan pengujian, dan mematikannya.

./gradlew pixel2api30DebugAndroidTest

Perangkat yang dikelola Gradle berisi mekanisme untuk mencoba lagi jika terjadi pemutusan koneksi perangkat dan peningkatan lainnya.

Mencegah masalah sinkronisasi

Komponen yang melakukan operasi latar belakang atau asinkron dapat menyebabkan kegagalan pengujian karena pernyataan pengujian dieksekusi sebelum UI siap untuknya. Seiring meningkatnya cakupan pengujian, kemungkinan pengujian menjadi tidak stabil juga meningkat. Masalah sinkronisasi ini adalah sumber utama ketidakstabilan karena framework pengujian perlu menyimpulkan apakah aktivitas selesai dimuat atau apakah harus menunggu lebih lama.

Solusi

Anda dapat menggunakan resource idling Espresso untuk menunjukkan kapan aplikasi sibuk, tetapi sulit untuk melacak setiap operasi asinkron, terutama dalam pengujian menyeluruh yang sangat besar. Selain itu, resource nonaktif dapat sulit diinstal tanpa polusi kode yang sedang diuji.

Daripada memperkirakan apakah aktivitas sibuk atau tidak, Anda dapat membuat pengujian menunggu hingga kondisi tertentu terpenuhi. Misalnya, Anda dapat menunggu hingga teks atau komponen tertentu ditampilkan di UI.

Compose memiliki kumpulan API pengujian sebagai bagian dari ComposeTestRule untuk menunggu matcher yang berbeda:

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)

Dan API generik yang menggunakan fungsi apa pun yang menampilkan boolean:

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

Contoh penggunaan:

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

Mekanisme percobaan ulang

Anda harus memperbaiki pengujian yang tidak stabil, tetapi terkadang kondisi yang membuatnya gagal sangat tidak mungkin sehingga sulit direproduksi. Meskipun Anda harus selalu melacak dan memperbaiki pengujian yang tidak stabil, mekanisme percobaan ulang dapat membantu mempertahankan produktivitas developer dengan menjalankan pengujian beberapa kali hingga lulus.

Percobaan ulang harus dilakukan di beberapa tingkat untuk mencegah masalah, seperti:

  • Waktu tunggu untuk menyambungkan ke perangkat habis atau koneksi terputus
  • Kegagalan satu pengujian

Menginstal atau mengonfigurasi percobaan ulang bergantung pada framework dan infrastruktur pengujian Anda, tetapi mekanisme umumnya mencakup:

  • Aturan JUnit yang mencoba ulang pengujian beberapa kali
  • Tindakan atau langkah percobaan ulang dalam alur kerja CI Anda
  • Sistem untuk memulai ulang emulator saat tidak responsif, seperti perangkat yang dikelola Gradle.